fix(renderer): migrate File.path reads to webUtils.getPathForFile for Electron 41

This commit is contained in:
2026-08-23 19:31:33 +05:30
parent 5fcc282fe0
commit c6ec1cef64
8 changed files with 185 additions and 22 deletions
+38
View File
@@ -0,0 +1,38 @@
/**
* Tests for the File-input path resolution helper (Electron 41 migration).
* File.path was removed in Electron 32; getFilePath() routes through
* window.electronAPI.getFilePath (webUtils.getPathForFile) when the bridge is
* available and falls back to file.path otherwise (older Electron, jsdom).
*/
const { getFilePath } = require('../src/utils/file-path');
describe('getFilePath helper', () => {
const originalAPI = window.electronAPI;
afterEach(() => {
window.electronAPI = originalAPI;
});
it('delegates to window.electronAPI.getFilePath when exposed', () => {
const file = { path: '/stale/file.path' };
window.electronAPI = { getFilePath: jest.fn(() => '/resolved/report.md') };
expect(getFilePath(file)).toBe('/resolved/report.md');
expect(window.electronAPI.getFilePath).toHaveBeenCalledWith(file);
});
it('falls back to file.path when the helper is absent', () => {
const file = { path: '/legacy/electron/file.md' };
window.electronAPI = { send: jest.fn() }; // no getFilePath on the surface
expect(getFilePath(file)).toBe('/legacy/electron/file.md');
});
it('falls back to file.path when electronAPI is undefined', () => {
const file = { path: '/no-bridge/file.md' };
window.electronAPI = undefined;
expect(getFilePath(file)).toBe('/no-bridge/file.md');
});
});
+61
View File
@@ -182,4 +182,65 @@ describe('Preload Security', () => {
expect(window.electronAPI.pdf.getPageCount).toBeDefined();
});
});
// Loads the real src/preload.js against a mocked electron module, following
// the jest.mock('electron') pattern used by the renderer dialog tests.
describe('getFilePath (webUtils.getPathForFile bridge)', () => {
const setupApi = window.electronAPI;
afterAll(() => {
window.electronAPI = setupApi;
});
test('exposes getFilePath and delegates to webUtils.getPathForFile', () => {
jest.mock('electron', () => ({
contextBridge: {
exposeInMainWorld: (key, api) => {
window[key] = api;
},
},
ipcRenderer: {
send: jest.fn(),
invoke: jest.fn(),
on: jest.fn(),
once: jest.fn(),
removeListener: jest.fn(),
removeAllListeners: jest.fn(),
},
webUtils: {
getPathForFile: jest.fn((file) => file && `/resolved${file.name}`),
},
}));
const { webUtils } = require('electron');
require('../src/preload.js');
expect(typeof window.electronAPI.getFilePath).toBe('function');
const file = { name: '/report.md' };
expect(window.electronAPI.getFilePath(file)).toBe('/resolved/report.md');
expect(webUtils.getPathForFile).toHaveBeenCalledWith(file);
});
test('falls back to file.path when webUtils is unavailable', () => {
jest.mock('electron', () => ({
contextBridge: {
exposeInMainWorld: (key, api) => {
window[key] = api;
},
},
ipcRenderer: {
send: jest.fn(),
invoke: jest.fn(),
on: jest.fn(),
once: jest.fn(),
removeListener: jest.fn(),
removeAllListeners: jest.fn(),
},
}));
require('../src/preload.js');
expect(typeof window.electronAPI.getFilePath).toBe('function');
const file = { path: '/legacy/file.md' };
expect(window.electronAPI.getFilePath(file)).toBe('/legacy/file.md');
});
});
});
+2
View File
@@ -11,6 +11,8 @@ global.window.electronAPI = {
once: jest.fn(),
invoke: jest.fn(() => Promise.resolve(null)),
removeAllListeners: jest.fn(),
// webUtils.getPathForFile bridge (File.path was removed in Electron 32)
getFilePath: jest.fn((file) => file && file.path),
file: {
save: jest.fn(),
saveCurrent: jest.fn(),