From b034fa7026d6c5e39bd24515ee6a1c038307ebe7 Mon Sep 17 00:00:00 2001 From: ethernet Date: Fri, 10 Jul 2026 12:33:45 -0400 Subject: [PATCH] test(desktop): convert zoom source-regex test to behavior test Extracted zoomWiringForWindowKind() + ZOOM_WINDOW_CONFIG into zoom.ts so the pet-overlay- opts-out / chat-windows-keep-zoom contract is tested via the pure config, not by reading source. Callers in main.ts now use zoomWiringForWindowKind() instead of inline { zoom: false } / default { zoom: true }. --- apps/desktop/electron/main.ts | 11 ++++---- apps/desktop/electron/zoom.test.ts | 45 +++++++++++++----------------- apps/desktop/electron/zoom.ts | 18 ++++++++++++ 3 files changed, 44 insertions(+), 30 deletions(-) diff --git a/apps/desktop/electron/main.ts b/apps/desktop/electron/main.ts index dced14e4e..7e5c64278 100644 --- a/apps/desktop/electron/main.ts +++ b/apps/desktop/electron/main.ts @@ -4709,7 +4709,8 @@ import { installZoomReassertOnWindowEvents, percentToZoomLevel, ZOOM_STORAGE_KEY, - zoomLevelToPercent + zoomLevelToPercent, + zoomWiringForWindowKind } from './zoom' function setAndPersistZoomLevel(window, zoomLevel) { @@ -6995,7 +6996,7 @@ function spawnSecondaryWindow({ win.on('enter-full-screen', () => sendWindowStateChanged(true)) win.on('leave-full-screen', () => sendWindowStateChanged(false)) - wireCommonWindowHandlers(win) + wireCommonWindowHandlers(win, zoomWiringForWindowKind('chat')) win.loadURL( buildSessionWindowUrl(sessionId, { @@ -7105,9 +7106,9 @@ function spawnPetOverlayWindow(bounds) { // Not supported everywhere — best effort. } - // Pet overlay opts out of global UI zoom (see wireCommonWindowHandlers): it + // Pet overlay opts out of global UI zoom (see zoomWiringForWindowKind): it // owns its window-fit + scale, and inheriting zoom would crop the sprite. - wireCommonWindowHandlers(win, { zoom: false }) + wireCommonWindowHandlers(win, zoomWiringForWindowKind('petOverlay')) win.once('ready-to-show', () => { if (!win.isDestroyed()) { @@ -7239,7 +7240,7 @@ function createWindow() { // window-all-closed from quitting on Windows/Linux). mainWindow.on('closed', () => closePetOverlay()) - wireCommonWindowHandlers(mainWindow) + wireCommonWindowHandlers(mainWindow, zoomWiringForWindowKind('chat')) mainWindow.webContents.on('render-process-gone', (_event, details) => { rememberLog(`[renderer] render-process-gone reason=${details?.reason} exitCode=${details?.exitCode}`) diff --git a/apps/desktop/electron/zoom.test.ts b/apps/desktop/electron/zoom.test.ts index 9401ca53f..f965a455b 100644 --- a/apps/desktop/electron/zoom.test.ts +++ b/apps/desktop/electron/zoom.test.ts @@ -5,10 +5,8 @@ */ import assert from 'node:assert/strict' -import fs from 'node:fs' -import path from 'node:path' + import { test } from 'vitest' -import { fileURLToPath } from 'node:url' import { clampZoomLevel, @@ -16,7 +14,8 @@ import { percentToZoomLevel, ZOOM_REASSERT_WINDOW_EVENTS, ZOOM_STORAGE_KEY, - zoomLevelToPercent + zoomLevelToPercent, + zoomWiringForWindowKind } from './zoom' test('storage key stays stable so persisted zoom survives upgrades', () => { @@ -66,12 +65,14 @@ test('extreme percentages clamp to the level bounds', () => { test('installZoomReassertOnWindowEvents wires show and restore', () => { const handlers = new Map() + const win = { isDestroyed: () => false, on(event, listener) { handlers.set(event, listener) } } + let calls = 0 installZoomReassertOnWindowEvents(win, () => { calls += 1 @@ -86,12 +87,14 @@ test('installZoomReassertOnWindowEvents wires show and restore', () => { test('installZoomReassertOnWindowEvents skips destroyed windows', () => { const handlers = new Map() let destroyed = false + const win = { isDestroyed: () => destroyed, on(event, listener) { handlers.set(event, listener) } } + let calls = 0 installZoomReassertOnWindowEvents(win, () => { calls += 1 @@ -101,25 +104,17 @@ test('installZoomReassertOnWindowEvents skips destroyed windows', () => { assert.equal(calls, 0) }) -// Source assertion (see windows-child-process.test.ts for the established -// pattern): wireCommonWindowHandlers lives in the electron main entry with heavy -// Electron deps, so we assert the wiring contract against source rather than -// booting a BrowserWindow. Locks in that the pet overlay opts OUT of global UI -// zoom while chat windows keep it — the whole reason this fix is scoped. -test('pet overlay opts out of global UI zoom; chat windows keep it', () => { - const electronDir = path.dirname(fileURLToPath(import.meta.url)) - const source = fs.readFileSync(path.join(electronDir, 'main.ts'), 'utf8').replace(/\r\n/g, '\n') - - // The shared helper gates all zoom wiring behind an opt-out flag. - assert.match(source, /function wireCommonWindowHandlers\(win, \{ zoom = true \}/) - - // The pet overlay window is the only caller that disables zoom. - assert.match(source, /wireCommonWindowHandlers\(win, \{ zoom: false \}\)/) - - // Zoom restore now flows through the shared helper, so createWindow must not - // reassert it directly (that would double-fire and drift from session windows). - const finishLoad = source.indexOf("mainWindow.webContents.once('did-finish-load'") - assert.notEqual(finishLoad, -1, 'missing mainWindow did-finish-load handler') - const snippet = source.slice(finishLoad, finishLoad + 300) - assert.doesNotMatch(snippet, /restorePersistedZoomLevel\(mainWindow\)/) +// Zoom-wiring contract: chat windows keep global UI zoom, the pet overlay +// opts out. Tested via the extracted config — no source-text regex. +test('chat windows opt into zoom', () => { + assert.deepEqual(zoomWiringForWindowKind('chat'), { zoom: true }) +}) + +test('pet overlay opts out of zoom', () => { + assert.deepEqual(zoomWiringForWindowKind('petOverlay'), { zoom: false }) +}) + +test('unknown window kinds default to chat (zoom enabled)', () => { + assert.deepEqual(zoomWiringForWindowKind('unknown'), { zoom: true }) + assert.deepEqual(zoomWiringForWindowKind(undefined), { zoom: true }) }) diff --git a/apps/desktop/electron/zoom.ts b/apps/desktop/electron/zoom.ts index 31182ea64..d4d769b7f 100644 --- a/apps/desktop/electron/zoom.ts +++ b/apps/desktop/electron/zoom.ts @@ -46,7 +46,25 @@ export function installZoomReassertOnWindowEvents(win, reassert) { if (win.isDestroyed?.()) { return } + reassert() }) } } + +/** + * Zoom-wiring decision per window kind. Chat windows (main + session) keep + * global UI zoom; the pet overlay opts out because it sizes its own OS window + * to the sprite and inheriting zoom would crop it. + * + * Extracted so the "pet opts out, everything else opts in" contract is + * unit-testable without booting a BrowserWindow or reading source. + */ +export const ZOOM_WINDOW_CONFIG = { + chat: { zoom: true }, + petOverlay: { zoom: false } +} as const + +export function zoomWiringForWindowKind(kind) { + return ZOOM_WINDOW_CONFIG[kind] ?? ZOOM_WINDOW_CONFIG.chat +}