[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
227
electron/bridges/windowManagerAutoLaunch.test.cjs
Normal file
227
electron/bridges/windowManagerAutoLaunch.test.cjs
Normal file
@@ -0,0 +1,227 @@
|
||||
const test = require("node:test");
|
||||
const assert = require("node:assert/strict");
|
||||
|
||||
const { createMainWindowApi } = require("./windowManager/mainWindow.cjs");
|
||||
|
||||
class BrowserWindowStub {
|
||||
constructor() {
|
||||
this._listeners = new Map();
|
||||
this.webContents = {
|
||||
id: 1,
|
||||
on() {},
|
||||
once() {},
|
||||
isDestroyed() {
|
||||
return false;
|
||||
},
|
||||
isCrashed() {
|
||||
return false;
|
||||
},
|
||||
setIgnoreMenuShortcuts() {},
|
||||
setWindowOpenHandler() {},
|
||||
openDevTools() {},
|
||||
getZoomFactor() {
|
||||
return 1;
|
||||
},
|
||||
setZoomFactor() {},
|
||||
};
|
||||
}
|
||||
|
||||
on(event, handler) {
|
||||
if (!this._listeners.has(event)) this._listeners.set(event, []);
|
||||
this._listeners.get(event).push(handler);
|
||||
}
|
||||
|
||||
once() {}
|
||||
isDestroyed() { return false; }
|
||||
isMaximized() { return false; }
|
||||
isFullScreen() { return false; }
|
||||
getBounds() { return { x: 0, y: 0, width: 1400, height: 900 }; }
|
||||
setBackgroundColor() {}
|
||||
setOpacity() {}
|
||||
async loadURL() {}
|
||||
close() {}
|
||||
|
||||
/** Test helper: fire a registered event listener, e.g. simulating win.show(). */
|
||||
emit(event) {
|
||||
for (const handler of this._listeners.get(event) || []) handler();
|
||||
}
|
||||
}
|
||||
|
||||
function createApi({ setupDeferredShow, getGlobalShortcutBridge } = {}) {
|
||||
return createMainWindowApi({
|
||||
mainWindow: null,
|
||||
electronApp: null,
|
||||
currentTheme: "light",
|
||||
isQuitting: false,
|
||||
pendingWindowStateWrite: null,
|
||||
queuedWindowState: null,
|
||||
windowStateCloseRequested: false,
|
||||
DEFAULT_WINDOW_WIDTH: 1400,
|
||||
DEFAULT_WINDOW_HEIGHT: 900,
|
||||
MIN_WINDOW_WIDTH: 1100,
|
||||
MIN_WINDOW_HEIGHT: 640,
|
||||
V8_CACHE_OPTIONS: "bypassHeatCheck",
|
||||
THEME_COLORS: { light: { background: "#fff" } },
|
||||
unhealthyWebContentsIds: new Set(),
|
||||
rendererReadySeenByWebContentsId: new Set(),
|
||||
__dirname,
|
||||
URL,
|
||||
require,
|
||||
console,
|
||||
setTimeout,
|
||||
clearTimeout,
|
||||
getGlobalShortcutBridge: getGlobalShortcutBridge || (() => ({ handleWindowClose: () => false })),
|
||||
debugLog() {},
|
||||
resolveFrontendBackgroundColor() { return null; },
|
||||
loadWindowState() { return null; },
|
||||
getDevRendererBaseUrl(url) { return url; },
|
||||
getWindowBoundsState() { return null; },
|
||||
queueWindowStateSave() {},
|
||||
saveWindowStateSync() {},
|
||||
setupDeferredShow: setupDeferredShow || (() => {}),
|
||||
createExternalOnlyWindowOpenHandler() { return {}; },
|
||||
createAppWindowOpenHandler() { return {}; },
|
||||
attachOAuthLoadingOverlay() {},
|
||||
registerWindowHandlers() {},
|
||||
requestWindowCommandClose() { return true; },
|
||||
shouldCloseWindowFromInput() { return false; },
|
||||
applyWindowOpacityToWindow() {},
|
||||
closeSettingsWindow() {},
|
||||
hideSettingsWindow() {},
|
||||
});
|
||||
}
|
||||
|
||||
async function createWindowWith(api, { startHidden, onRegisterBridge } = {}) {
|
||||
return api.createWindow(
|
||||
{
|
||||
BrowserWindow: BrowserWindowStub,
|
||||
nativeTheme: {},
|
||||
app: {},
|
||||
screen: {},
|
||||
shell: {},
|
||||
ipcMain: {},
|
||||
},
|
||||
{
|
||||
preload: "/tmp/preload.cjs",
|
||||
devServerUrl: "http://localhost:5173",
|
||||
isDev: true,
|
||||
appIcon: null,
|
||||
isMac: false,
|
||||
electronDir: __dirname,
|
||||
onRegisterBridge,
|
||||
startHidden,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
test("createWindow forwards startHidden to setupDeferredShow", async () => {
|
||||
const deferredShowCalls = [];
|
||||
const api = createApi({
|
||||
setupDeferredShow: (win, options) => { deferredShowCalls.push(options); },
|
||||
});
|
||||
|
||||
await createWindowWith(api, { startHidden: true });
|
||||
|
||||
assert.equal(deferredShowCalls.length, 1);
|
||||
assert.equal(deferredShowCalls[0].startHidden, true);
|
||||
});
|
||||
|
||||
test("createWindow defaults startHidden to false when omitted", async () => {
|
||||
const deferredShowCalls = [];
|
||||
const api = createApi({
|
||||
setupDeferredShow: (win, options) => { deferredShowCalls.push(options); },
|
||||
});
|
||||
|
||||
await createWindowWith(api, {});
|
||||
|
||||
assert.equal(deferredShowCalls.length, 1);
|
||||
assert.equal(deferredShowCalls[0].startHidden, false);
|
||||
});
|
||||
|
||||
test("createWindow(startHidden) pins the tray open right after bridges register", async () => {
|
||||
const callOrder = [];
|
||||
const api = createApi({
|
||||
getGlobalShortcutBridge: () => ({
|
||||
handleWindowClose: () => false,
|
||||
pinTrayForHiddenLaunch: () => { callOrder.push("pinTrayForHiddenLaunch"); },
|
||||
}),
|
||||
});
|
||||
|
||||
await createWindowWith(api, {
|
||||
startHidden: true,
|
||||
onRegisterBridge: () => { callOrder.push("onRegisterBridge"); },
|
||||
});
|
||||
|
||||
assert.deepEqual(callOrder, ["onRegisterBridge", "pinTrayForHiddenLaunch"]);
|
||||
});
|
||||
|
||||
test("createWindow(startHidden) tolerates a tray pin failure without throwing", async () => {
|
||||
const api = createApi({
|
||||
getGlobalShortcutBridge: () => ({
|
||||
handleWindowClose: () => false,
|
||||
pinTrayForHiddenLaunch: () => { throw new Error("electronModule not ready"); },
|
||||
}),
|
||||
});
|
||||
|
||||
await assert.doesNotReject(() => createWindowWith(api, { startHidden: true }));
|
||||
});
|
||||
|
||||
test("createWindow without startHidden does not pin the tray open", async () => {
|
||||
let pinCalls = 0;
|
||||
const api = createApi({
|
||||
getGlobalShortcutBridge: () => ({
|
||||
handleWindowClose: () => false,
|
||||
pinTrayForHiddenLaunch: () => { pinCalls += 1; },
|
||||
}),
|
||||
});
|
||||
|
||||
await createWindowWith(api, {});
|
||||
|
||||
assert.equal(pinCalls, 0);
|
||||
});
|
||||
|
||||
test("a hidden cold start releases the tray pin once the window is actually shown", async () => {
|
||||
let releaseCalls = 0;
|
||||
const api = createApi({
|
||||
getGlobalShortcutBridge: () => ({
|
||||
handleWindowClose: () => false,
|
||||
pinTrayForHiddenLaunch: () => {},
|
||||
releaseHiddenLaunchTrayPin: () => { releaseCalls += 1; },
|
||||
}),
|
||||
});
|
||||
|
||||
const win = await createWindowWith(api, { startHidden: true });
|
||||
assert.equal(releaseCalls, 0, "must stay pinned until the window is actually shown");
|
||||
|
||||
win.emit("show");
|
||||
assert.equal(releaseCalls, 1);
|
||||
});
|
||||
|
||||
test("a normal (non-hidden) cold start never touches the hidden-launch tray pin", async () => {
|
||||
let releaseCalls = 0;
|
||||
const api = createApi({
|
||||
getGlobalShortcutBridge: () => ({
|
||||
handleWindowClose: () => false,
|
||||
releaseHiddenLaunchTrayPin: () => { releaseCalls += 1; },
|
||||
}),
|
||||
});
|
||||
|
||||
const win = await createWindowWith(api, {});
|
||||
win.emit("show");
|
||||
|
||||
assert.equal(releaseCalls, 0);
|
||||
});
|
||||
|
||||
test("releasing the tray pin tolerates a failure without throwing", async () => {
|
||||
const api = createApi({
|
||||
getGlobalShortcutBridge: () => ({
|
||||
handleWindowClose: () => false,
|
||||
pinTrayForHiddenLaunch: () => {},
|
||||
releaseHiddenLaunchTrayPin: () => { throw new Error("tray already gone"); },
|
||||
}),
|
||||
});
|
||||
|
||||
const win = await createWindowWith(api, { startHidden: true });
|
||||
|
||||
assert.doesNotThrow(() => win.emit("show"));
|
||||
});
|
||||
Reference in New Issue
Block a user