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
228 lines
6.4 KiB
JavaScript
228 lines
6.4 KiB
JavaScript
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"));
|
|
});
|