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 }.
This commit is contained in:
ethernet 2026-07-10 12:33:45 -04:00
parent c3aa81be2f
commit b034fa7026
3 changed files with 44 additions and 30 deletions

View file

@ -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}`)

View file

@ -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 })
})

View file

@ -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
}