feedBack-desktop/tests/window-bounds.test.js
topkoa 030559dd38 fix(panes): only the main window may drive the tray; clamp fallback defaults
1. pane:sync was accepted from ANY renderer with the preload bridge. Pane
   windows are same-origin top-level frames, so preload's isMainFrame gate hands
   them the bridge too — which means a pane window (or any allowed pop-up) could
   send pane:sync and overwrite the tray's registry, most simply by pushing an
   empty list and emptying the menu.

   Exactly one renderer owns the pane registry. It is now accepted from that one
   only: event.sender must be the main window's webContents.

2. sanitizeWindowBounds returned sizing.defaultWidth/Height unclamped. The min
   clamp only runs when `saved` parses, so a caller whose defaults undercut its
   own minimums would get a window below the floor on precisely the paths where
   nothing is saved — first launch, or a corrupt config — and a correctly sized
   one everywhere else.

   That is the worst shape a bug can have: invisible in the common case, and
   visible only to a new user. The fallback is clamped to the floor now, with a
   test.

window-bounds: 14/14.
Signed-off-by: topkoa <topkoa@gmail.com>
2026-07-12 21:18:05 -04:00

113 lines
5.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const test = require('node:test');
const assert = require('node:assert/strict');
const { loadTs } = require('./_load-ts');
const { sanitizeWindowBounds, DEFAULT_WIDTH, DEFAULT_HEIGHT, MIN_WIDTH, MIN_HEIGHT } =
loadTs('src/main/window-bounds.ts');
// A common single-display workArea (1920×1080 minus a 40px taskbar).
const PRIMARY = { x: 0, y: 0, width: 1920, height: 1040 };
const DEFAULTS = { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT, maximized: false };
test('valid bounds on a matching display round-trip', () => {
const saved = { x: 100, y: 50, width: 1200, height: 800, maximized: true };
assert.deepEqual(sanitizeWindowBounds(saved, [PRIMARY]), saved);
});
test('garbage input falls back to defaults', () => {
for (const bad of [undefined, null, 'wat', 42, {}, { x: 1, y: 2 }, { x: 'a', y: 0, width: 1200, height: 800 }, { x: NaN, y: 0, width: 1200, height: 800 }, { x: 0, y: 0, width: Infinity, height: 800 }]) {
assert.deepEqual(sanitizeWindowBounds(bad, [PRIMARY]), DEFAULTS);
}
});
test('empty display list falls back to defaults', () => {
assert.deepEqual(sanitizeWindowBounds({ x: 0, y: 0, width: 1200, height: 800 }, []), DEFAULTS);
});
test('oversize bounds clamp to the largest display workArea', () => {
const out = sanitizeWindowBounds({ x: 0, y: 0, width: 5000, height: 4000 }, [PRIMARY]);
assert.deepEqual(out, { x: 0, y: 0, width: 1920, height: 1040, maximized: false });
});
test('undersize bounds clamp up to the window minimums', () => {
const out = sanitizeWindowBounds({ x: 10, y: 10, width: 300, height: 200 }, [PRIMARY]);
assert.deepEqual(out, { x: 10, y: 10, width: MIN_WIDTH, height: MIN_HEIGHT, maximized: false });
});
test('position on a now-unplugged monitor is dropped, size kept', () => {
// Saved on a second display to the right that no longer exists.
const out = sanitizeWindowBounds({ x: 2000, y: 100, width: 1200, height: 800 }, [PRIMARY]);
assert.deepEqual(out, { width: 1200, height: 800, maximized: false });
});
test('negative coordinates on a left-of-primary monitor are kept', () => {
const leftMonitor = { x: -1920, y: 0, width: 1920, height: 1040 };
const saved = { x: -1800, y: 100, width: 1200, height: 800 };
const out = sanitizeWindowBounds(saved, [leftMonitor, PRIMARY]);
assert.deepEqual(out, { ...saved, maximized: false });
});
test('sliver overlap below the grab threshold drops the position', () => {
// Only 50px of the window's left edge on screen — not enough to grab.
const out = sanitizeWindowBounds({ x: 1870, y: 100, width: 1200, height: 800 }, [PRIMARY]);
assert.deepEqual(out, { width: 1200, height: 800, maximized: false });
});
test('fractional coordinates are rounded to integers', () => {
const out = sanitizeWindowBounds({ x: 10.6, y: 20.4, width: 1200.5, height: 800.2 }, [PRIMARY]);
assert.deepEqual(out, { x: 11, y: 20, width: 1201, height: 800, maximized: false });
});
// ── Custom sizing (pane windows) ────────────────────────────────────────────
//
// sanitizeWindowBounds grew a `sizing` parameter so pane pop-outs could be small.
// Without it, a 380×560 pane restored through the MAIN window's 800×600 floor would
// be silently inflated to 800×600 — three times the size the plugin asked for. The
// default argument keeps every existing call site byte-for-byte identical, so these
// tests exist to pin the override itself, which is the part nothing else covers.
const PANE_SIZING = { minWidth: 240, minHeight: 180, defaultWidth: 380, defaultHeight: 560 };
test('custom sizing: a small pane is NOT inflated to the main window floor', () => {
const out = sanitizeWindowBounds({ x: 100, y: 100, width: 380, height: 560 }, [PRIMARY], PANE_SIZING);
assert.deepEqual(out, { x: 100, y: 100, width: 380, height: 560, maximized: false });
});
test('custom sizing: the min clamp uses the override, not MIN_WIDTH/MIN_HEIGHT', () => {
// Below the pane minimum (240×180) — clamped up to it, and nowhere near 800×600.
const out = sanitizeWindowBounds({ x: 10, y: 10, width: 50, height: 20 }, [PRIMARY], PANE_SIZING);
assert.deepEqual(out, { x: 10, y: 10, width: 240, height: 180, maximized: false });
});
test('custom sizing: missing/corrupt bounds fall back to the override defaults', () => {
assert.deepEqual(
sanitizeWindowBounds(undefined, [PRIMARY], PANE_SIZING),
{ width: 380, height: 560, maximized: false },
);
assert.deepEqual(
sanitizeWindowBounds({ x: 'nope', y: 0, width: 380, height: 560 }, [PRIMARY], PANE_SIZING),
{ width: 380, height: 560, maximized: false },
);
});
test('omitting sizing keeps the main window behaviour exactly as before', () => {
// The whole point of the default argument: existing call sites must not shift.
const out = sanitizeWindowBounds({ x: 10, y: 10, width: 50, height: 20 }, [PRIMARY]);
assert.deepEqual(out, { x: 10, y: 10, width: MIN_WIDTH, height: MIN_HEIGHT, maximized: false });
});
test('custom sizing: defaults below the configured minimum are clamped up', () => {
// A caller whose defaults undercut its own floor. The min clamp only runs on
// saved bounds, so without clamping the fallback too, the floor would hold
// everywhere EXCEPT first launch and a corrupt config — i.e. only for new users.
const silly = { minWidth: 240, minHeight: 180, defaultWidth: 100, defaultHeight: 50 };
assert.deepEqual(
sanitizeWindowBounds(undefined, [PRIMARY], silly),
{ width: 240, height: 180, maximized: false },
);
assert.deepEqual(
sanitizeWindowBounds({ x: 0, y: 0, width: 'bad', height: 'bad' }, [PRIMARY], silly),
{ width: 240, height: 180, maximized: false },
);
});