Jarian Cottingham 9b6e90ce0e 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

156 lines
3.2 KiB
JavaScript

import { vi } from 'vitest';
// Mock Electron modules globally
vi.mock('electron', () => ({
app: {
whenReady: vi.fn(() => Promise.resolve()),
on: vi.fn(),
quit: vi.fn(),
},
BrowserWindow: vi.fn().mockImplementation(() => ({
loadFile: vi.fn(),
webContents: {
send: vi.fn(),
},
onClose: vi.fn(),
})),
ipcMain: {
handle: vi.fn(),
on: vi.fn(),
},
dialog: {
showOpenDialog: vi.fn(),
},
contextBridge: {
exposeInMainWorld: vi.fn(),
},
ipcRenderer: {
invoke: vi.fn(),
on: vi.fn(),
},
}));
// Mock fluent-ffmpeg
vi.mock('fluent-ffmpeg', () => ({
ffprobe: vi.fn((filePath, callback) => {
callback(null, {
format: { duration: 65 },
streams: [
{
codec_type: 'video',
width: 1920,
height: 1080,
r_frame_rate: '30/1',
},
],
});
}),
}));
// Mock dotenv
vi.mock('dotenv', () => ({
config: vi.fn(),
}));
// Mock fs
vi.mock('fs', () => ({
default: {
existsSync: vi.fn(() => true),
readFileSync: vi.fn(() => ''),
writeFileSync: vi.fn(),
appendFileSync: vi.fn(),
statSync: vi.fn(() => ({
isFile: vi.fn(() => true),
isDirectory: vi.fn(() => false),
size: 1024,
mtime: new Date(),
})),
accessSync: vi.fn(),
renameSync: vi.fn(),
mkdirSync: vi.fn(),
rmSync: vi.fn(),
},
existsSync: vi.fn(() => true),
readFileSync: vi.fn(() => ''),
writeFileSync: vi.fn(),
appendFileSync: vi.fn(),
statSync: vi.fn(() => ({
isFile: vi.fn(() => true),
isDirectory: vi.fn(() => false),
size: 1024,
mtime: new Date(),
})),
accessSync: vi.fn(),
renameSync: vi.fn(),
mkdirSync: vi.fn(),
rmSync: vi.fn(),
constants: { W_OK: 2 },
promises: {
readdir: vi.fn(() => Promise.resolve(['file1.mp4', 'file2.mkv', 'folder1'])),
stat: vi.fn(() =>
Promise.resolve({
isFile: () => true,
isDirectory: () => false,
size: 1024,
mtime: new Date(),
})
),
},
}));
// Mock path
vi.mock('path', () => ({
default: {
join: (...args) => args.join('/'),
dirname: (p) => p.substring(0, p.lastIndexOf('/')),
basename: (p) => p.split('/').pop(),
extname: (p) => '.' + p.split('.').pop(),
sep: '/',
resolve: (...args) => args.join('/'),
},
join: (...args) => args.join('/'),
dirname: (p) => p.substring(0, p.lastIndexOf('/')),
basename: (p) => p.split('/').pop(),
extname: (p) => '.' + p.split('.').pop(),
sep: '/',
resolve: (...args) => args.join('/'),
}));
// Mock child_process
vi.mock('child_process', () => ({
execFile: vi.fn((cmd, args, callback) => {
callback(null, '', '');
}),
exec: vi.fn((cmd, callback) => {
callback(null, '', '');
}),
}));
// Mock axios
vi.mock('axios', () => ({
default: {
get: vi.fn(() =>
Promise.resolve({
data: {
data: {
id: 'series-12345',
name: 'Test Show',
seasons: [],
},
},
})
),
post: vi.fn(() =>
Promise.resolve({
data: {
data: {
token: 'test-token',
},
},
})
),
},
}));
globalThis.__dirname = '/tmp/MovieMapper';