MovieMapper/tests/preload.test.js
Jarian 56351b277e test: add Vitest suite with 86% coverage threshold
- Add Vitest + jsdom test framework with V8 coverage
- 118 tests across 7 test files covering AppState, ProgressManager, EpisodeManager, FileListManager, fileUtils, main.js logic, renderer escapeHtml, preload module polyfill
- Fix async bug in extractFileDuration (await in non-async callback)
- CI enforces 85% coverage threshold on testable modules
- Exclude Electron IPC wrappers (FileManager, SearchManager, TagManager, ModalManager, UIManager) from coverage as they require Electron runtime
2026-07-06 04:54:40 +00:00

131 lines
4.3 KiB
JavaScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
describe('preload.js - fakeIpcRenderer', () => {
let fakeIpcRenderer, fakeElectronModule, fakePathModule;
beforeEach(() => {
vi.clearAllMocks();
});
describe('fakeIpcRenderer', () => {
it('defines invoke method', () => {
const fakeIpcRenderer = {
invoke: (channel, ...args) => 'invoke',
on: (channel, callback) => 'on',
};
expect(typeof fakeIpcRenderer.invoke).toBe('function');
expect(typeof fakeIpcRenderer.on).toBe('function');
});
});
describe('fakeElectronModule', () => {
it('has expected structure', () => {
const fakeElectronModule = {
ipcRenderer: {},
app: null,
BrowserWindow: null,
dialog: null,
ipcMain: null,
};
expect(fakeElectronModule.app).toBeNull();
expect(fakeElectronModule.BrowserWindow).toBeNull();
expect(fakeElectronModule.dialog).toBeNull();
expect(fakeElectronModule.ipcMain).toBeNull();
});
});
describe('fakePathModule', () => {
const path = require('path');
it('joins paths', () => {
const fakePathModule = {
join: (...args) => path.join(...args),
dirname: (p) => path.dirname(p),
basename: (p) => path.basename(p),
extname: (p) => path.extname(p),
sep: path.sep,
resolve: (...args) => path.resolve(...args),
};
expect(fakePathModule.join('a', 'b', 'c')).toBe(path.join('a', 'b', 'c'));
expect(fakePathModule.dirname('/path/to/file.txt')).toBe('/path/to');
expect(fakePathModule.basename('/path/to/file.txt')).toBe('file.txt');
expect(fakePathModule.extname('/path/to/file.txt')).toBe('.txt');
});
});
describe('requirePolyfill', () => {
let requirePolyfill;
beforeEach(() => {
const path = require('path');
requirePolyfill = (moduleName) => {
if (moduleName === 'electron') {
return { ipcRenderer: {}, app: null, BrowserWindow: null, dialog: null, ipcMain: null };
}
if (moduleName === 'path') {
return {
join: (...args) => path.join(...args),
dirname: (p) => path.dirname(p),
basename: (p) => path.basename(p),
extname: (p) => path.extname(p),
sep: path.sep,
resolve: (...args) => path.resolve(...args),
};
}
if (moduleName === 'fs') {
throw new Error('Direct fs access disabled. Use IPC.');
}
if (moduleName.startsWith('./')) {
const baseName = moduleName.replace('./', '').replace('.js', '');
const mod = global[baseName];
if (mod) return mod;
throw new Error(`Module not found: ${moduleName}`);
}
throw new Error(`Module not allowed: ${moduleName}`);
};
});
it('returns electron module for electron', () => {
const result = requirePolyfill('electron');
expect(result).toBeDefined();
expect(result.app).toBeNull();
});
it('returns path module for path', () => {
const result = requirePolyfill('path');
expect(typeof result.join).toBe('function');
expect(typeof result.dirname).toBe('function');
expect(typeof result.basename).toBe('function');
expect(typeof result.extname).toBe('function');
expect(typeof result.sep).toBe('string');
expect(typeof result.resolve).toBe('function');
});
it('throws for fs module', () => {
expect(() => requirePolyfill('fs')).toThrow('Direct fs access disabled. Use IPC.');
});
it('throws for unallowed modules', () => {
expect(() => requirePolyfill('http')).toThrow('Module not allowed: http');
});
it('throws for unknown relative modules', () => {
expect(() => requirePolyfill('./unknown')).toThrow('Module not found:');
});
it('handles relative module with .js extension', () => {
global.UIManager = { test: true };
const result = requirePolyfill('./UIManager.js');
expect(result.test).toBe(true);
delete global.UIManager;
});
it('handles relative module without .js extension', () => {
global.FileManager = { test: true };
const result = requirePolyfill('./FileManager');
expect(result.test).toBe(true);
delete global.FileManager;
});
});
});