super-productivity/electron/indicator.test.cjs
felix bear 376e068412
fix(electron): respect tray title settings #7823 (#8097)
Co-authored-by: cocojojo5213 <cocojojo5213@users.noreply.github.com>
2026-06-08 12:07:09 +02:00

268 lines
6.6 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const path = require('node:path');
const Module = require('node:module');
require('ts-node/register/transpile-only');
const originalModuleLoad = Module._load;
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, 'platform');
const indicatorModulePath = path.resolve(__dirname, 'indicator.ts');
let createdTrayArgs;
let createdFromPath = [];
let traySetImageCalls = [];
let traySetTitleCalls = [];
let traySetToolTipCalls = [];
let ipcHandlers = new Map();
let nextNativeImageIsEmpty = false;
let beforeQuitHandler = () => {};
let mockIsTrayShowCurrentTask = false;
let mockIsTrayShowCurrentCountdown = false;
const resetModule = () => {
delete require.cache[indicatorModulePath];
};
const installMocks = () => {
Module._load = function patchedLoad(request, parent, isMain) {
if (request === 'electron') {
class FakeTray {
constructor(image, guid) {
createdTrayArgs.push([image, guid]);
}
setContextMenu() {}
on(eventName, handler) {
if (eventName === 'click') {
this._clickHandler = handler;
}
}
setImage(image) {
traySetImageCalls.push(image);
}
setTitle(title) {
traySetTitleCalls.push(title);
}
setToolTip(title) {
traySetToolTipCalls.push(title);
}
destroy() {}
}
return {
Tray: FakeTray,
Menu: {
buildFromTemplate: (template) => ({ template }),
},
ipcMain: {
on: (eventName, handler) => {
ipcHandlers.set(eventName, handler);
},
},
nativeTheme: {
shouldUseDarkColors: false,
},
nativeImage: {
createFromPath: (iconPath) => {
createdFromPath.push(iconPath);
return {
iconPath,
kind: 'native-image',
isEmpty: () => nextNativeImageIsEmpty,
setTemplateImage: () => {},
};
},
},
};
}
if (request === 'electron-log/main') {
return {
log: () => {},
};
}
if (request === './shared-with-frontend/ipc-events.const') {
return {
IPC: {
UPDATE_SETTINGS: 'UPDATE_SETTINGS',
SET_PROGRESS_BAR: 'SET_PROGRESS_BAR',
CURRENT_TASK_UPDATED: 'CURRENT_TASK_UPDATED',
TODAY_TASKS_UPDATED: 'TODAY_TASKS_UPDATED',
TASK_TOGGLE_START: 'TASK_TOGGLE_START',
SWITCH_TASK: 'SWITCH_TASK',
},
};
}
if (request === './shared-state') {
return {
getIsTrayShowCurrentTask: () => mockIsTrayShowCurrentTask,
getIsTrayShowCurrentCountdown: () => mockIsTrayShowCurrentCountdown,
};
}
if (request === './task-widget/task-widget') {
return {
updateTaskWidgetAlwaysShow: () => {},
updateTaskWidgetEnabled: () => {},
updateTaskWidgetOpacity: () => {},
updateTaskWidgetTask: () => {},
initTaskWidgetSettingsListener: () => {},
};
}
if (request === './main-window') {
return {
getWin: () => undefined,
};
}
if (
request === '../src/app/features/tasks/task.model' ||
request === '../src/app/features/config/global-config.model'
) {
return {};
}
return originalModuleLoad.call(this, request, parent, isMain);
};
};
const loadIndicatorModule = () => {
resetModule();
return require(indicatorModulePath);
};
test.beforeEach(() => {
createdTrayArgs = [];
createdFromPath = [];
traySetImageCalls = [];
traySetTitleCalls = [];
traySetToolTipCalls = [];
ipcHandlers = new Map();
nextNativeImageIsEmpty = false;
beforeQuitHandler = () => {};
mockIsTrayShowCurrentTask = false;
mockIsTrayShowCurrentCountdown = false;
Object.defineProperty(process, 'platform', {
configurable: true,
value: 'linux',
});
installMocks();
});
test.afterEach(() => {
Module._load = originalModuleLoad;
Object.defineProperty(process, 'platform', originalPlatformDescriptor);
resetModule();
});
test('initIndicator uses NativeImage for Linux tray creation and updates', () => {
const { initIndicator } = loadIndicatorModule();
initIndicator({
showApp: () => {},
quitApp: () => {},
ICONS_FOLDER: '/icons/',
forceDarkTray: false,
app: {
on: (eventName, handler) => {
if (eventName === 'before-quit') {
beforeQuitHandler = handler;
}
},
},
});
assert.equal(createdTrayArgs.length, 1);
assert.equal(createdTrayArgs[0][0].kind, 'native-image');
assert.match(createdTrayArgs[0][0].iconPath, /\/icons\/indicator\/stopped-d\.png$/);
assert.match(createdFromPath[0], /\/icons\/indicator\/stopped-d\.png$/);
const currentTaskUpdated = ipcHandlers.get('CURRENT_TASK_UPDATED');
assert.equal(typeof currentTaskUpdated, 'function');
currentTaskUpdated(
{},
{ id: 'T1', title: 'Task', timeSpent: 5 * 60000, timeEstimate: 25 * 60000 },
false,
0,
false,
0,
undefined,
);
assert.equal(traySetImageCalls.at(-1).kind, 'native-image');
assert.match(
traySetImageCalls.at(-1).iconPath,
/\/icons\/indicator\/running-anim-d\/3\.png$/,
);
beforeQuitHandler();
});
test('initIndicator falls back to icon path if NativeImage creation is empty', () => {
nextNativeImageIsEmpty = true;
const { initIndicator } = loadIndicatorModule();
initIndicator({
showApp: () => {},
quitApp: () => {},
ICONS_FOLDER: '/icons/',
forceDarkTray: false,
app: {
on: () => {},
},
});
assert.equal(createdTrayArgs.length, 1);
assert.equal(typeof createdTrayArgs[0][0], 'string');
assert.match(createdTrayArgs[0][0], /\/icons\/indicator\/stopped-d\.png$/);
});
test('tray title shows the task title when countdown display is disabled', () => {
mockIsTrayShowCurrentTask = true;
mockIsTrayShowCurrentCountdown = false;
const { initIndicator } = loadIndicatorModule();
initIndicator({
showApp: () => {},
quitApp: () => {},
ICONS_FOLDER: '/icons/',
forceDarkTray: false,
app: {
on: () => {},
},
});
const currentTaskUpdated = ipcHandlers.get('CURRENT_TASK_UPDATED');
assert.equal(typeof currentTaskUpdated, 'function');
currentTaskUpdated(
{},
{
id: 'T1',
title: 'Write release notes',
timeSpent: 5 * 60000,
timeEstimate: 25 * 60000,
},
false,
0,
false,
0,
undefined,
);
assert.equal(traySetTitleCalls.at(-1), 'Write release notes');
assert.equal(traySetToolTipCalls.at(-1), 'Write release notes');
});