Skip to content
This repository was archived by the owner on Feb 17, 2025. It is now read-only.

Commit d416360

Browse files
committed
Refactor access modifiers in ffmpeg.ts and jimp.ts
1 parent aed0425 commit d416360

11 files changed

+154
-160
lines changed

src/lib/conversion/conversion-handler.ts

+21-16
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import { AudioFormat } from '@/enum/audio-format';
55
import { ImageFormat } from '@/enum/image-format';
66
import { FfmpegAdapter } from './ffmpeg';
77
import { JimpAdapter } from './jimp';
8+
import { Adapter } from '@/types/adapter';
89

910
export class ConversionHandler {
10-
protected conversions: Map<string, FfmpegAdapter | JimpAdapter> = new Map();
11+
protected conversions: Map<string, Adapter> = new Map();
1112

1213
/**
1314
* Adds a new conversion to the handler.
1415
*/
15-
handle(
16+
async handle(
1617
id: string,
1718
filePath: string,
1819
outputFormat: VideoFormat | AudioFormat | ImageFormat,
@@ -22,28 +23,32 @@ export class ConversionHandler {
2223
): Promise<string> {
2324
const adapter = this.getAdapter(type);
2425

25-
return new Promise((resolve, reject) => {
26-
this.conversions.set(id, adapter);
26+
this.conversions.set(id, adapter);
2727

28-
adapter
29-
.convert(id, filePath, outputFormat, saveDirectory, event)
30-
.then(() => {
31-
this.conversions.delete(id);
32-
resolve(filePath);
33-
})
34-
.catch((error) => {
35-
this.conversions.delete(id);
36-
reject(error);
37-
});
38-
});
28+
try {
29+
const result = await adapter.convert(id, filePath, outputFormat, saveDirectory, event);
30+
31+
this.conversions.delete(id);
32+
33+
return result;
34+
} catch (error) {
35+
this.conversions.delete(id);
36+
37+
throw error;
38+
}
3939
}
4040

4141
/**
4242
* Cancels the conversion with the given ID.
4343
*/
4444
cancel(id: string): boolean {
4545
const adapter = this.conversions.get(id);
46-
return adapter ? adapter.cancel(id) : false;
46+
if (adapter) {
47+
const result = adapter.cancel(id);
48+
this.conversions.delete(id);
49+
return result;
50+
}
51+
return false;
4752
}
4853

4954
/**

src/lib/conversion/ffmpeg.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export class FfmpegAdapter implements Adapter {
2020
/**
2121
* Map of FFmpeg processes by ID.
2222
*/
23-
private ffmpegProcesses = new Map<string, ffmpeg.FfmpegCommand>();
23+
protected ffmpegProcesses = new Map<string, ffmpeg.FfmpegCommand>();
2424

2525
/**
2626
* Converts the file at the given path to the specified output format.

src/lib/conversion/jimp.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Adapter } from '@/types/adapter';
66
export type JimpType = typeof Jimp;
77

88
export class JimpAdapter implements Adapter {
9-
private jimpProcesses = new Map<string, JimpType>();
9+
protected jimpProcesses = new Map<string, JimpType>();
1010

1111
/**
1212
* Converts the file at the given path to the specified output format.
@@ -21,7 +21,6 @@ export class JimpAdapter implements Adapter {
2121
const outputFileName = `${path.basename(filePath, path.extname(filePath))}.${outputFormat}`;
2222
const outputPath = path.join(saveDirectory, outputFileName);
2323

24-
// Ensure save directory exists
2524
await fs.mkdir(saveDirectory, { recursive: true });
2625

2726
const image = await Jimp.read(filePath);

src/lib/system/ipc-handlers.ts

+2-8
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,11 @@ export function configureIpcHandlers(ipcMain: IpcMain): void {
4242
mediaType: Media;
4343
},
4444
) => {
45-
return new Promise<string>((resolve, reject) => {
46-
conversionHandler
47-
.handle(id, filePath, outputFormat, saveDirectory, mediaType, event)
48-
.then(resolve)
49-
.catch(reject);
50-
});
45+
return await conversionHandler.handle(id, filePath, outputFormat, saveDirectory, mediaType, event);
5146
},
5247
);
5348

54-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
55-
ipcMain.handle(IpcEvent.CANCEL_CONVERSION, (_event: IpcMainInvokeEvent) => {
49+
ipcMain.handle(IpcEvent.CANCEL_CONVERSION, () => {
5650
return conversionHandler.cancelAll();
5751
});
5852

tests/main/conversion-handler.test.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,18 @@ describe('ConversionHandler', () => {
9696
});
9797

9898
test('should reject unsupported media type', async () => {
99-
await expect(
100-
conversionHandler.handle(
99+
try {
100+
await conversionHandler.handle(
101101
'1',
102102
'/mock/path/unknown.file',
103103
'unknown_format' as VideoFormat,
104104
'/mock/save',
105105
'unknown' as Media,
106106
mockEvent
107-
)
108-
).rejects.toThrow('Unsupported type: unknown');
107+
);
108+
} catch (error) {
109+
expect(error).toEqual(new Error('Unsupported type: unknown'));
110+
}
109111
});
110112

111113
test('should handle conversion failure', async () => {
@@ -137,7 +139,8 @@ describe('ConversionHandler', () => {
137139
const mockJimpAdapter = jest.spyOn(JimpAdapter.prototype, 'cancel').mockReturnValue(true);
138140

139141
// Mock the process is already stored in the map
140-
conversionHandler['conversions'].set('1', new JimpAdapter());
142+
const jimpAdapterInstance = new JimpAdapter();
143+
conversionHandler['conversions'].set('1', jimpAdapterInstance);
141144

142145
const result = conversionHandler.cancel('1');
143146

tests/main/ipc-handler.test.ts

+90-98
Original file line numberDiff line numberDiff line change
@@ -1,141 +1,133 @@
1-
/**
2-
* @jest-environment node
3-
*/
4-
5-
import { configureIpcHandlers } from '../../src/lib/system/ipc-handlers';
6-
import { IpcEvent } from '../../src/enum/ipc-event';
7-
import { dialog, ipcMain, IpcMainInvokeEvent } from 'electron';
8-
import { getDesktopPath } from '../../src/lib/utils/desktop-path';
9-
import { ConversionHandler } from '../../src/lib/conversion/conversion-handler';
10-
11-
jest.mock('../../src/lib/utils/desktop-path');
12-
jest.mock('../../src/lib/conversion/conversion-handler');
1+
import { dialog, IpcMainInvokeEvent } from 'electron';
2+
import { getDesktopPath } from '@/lib/utils/desktop-path';
3+
import { IpcEvent } from '@/enum/ipc-event';
4+
import { ConversionHandler } from '@/lib/conversion/conversion-handler';
5+
import { configureIpcHandlers } from '@/lib/system/ipc-handlers';
6+
7+
// Mock the dependencies
8+
jest.mock('electron', () => ({
9+
dialog: {
10+
showOpenDialog: jest.fn(),
11+
},
12+
}));
13+
jest.mock('@/lib/utils/desktop-path', () => ({
14+
getDesktopPath: jest.fn(),
15+
}));
16+
jest.mock('@/lib/conversion/conversion-handler', () => {
17+
return {
18+
ConversionHandler: jest.fn().mockImplementation(() => ({
19+
handle: jest.fn(),
20+
cancelAll: jest.fn(),
21+
cancel: jest.fn(),
22+
})),
23+
};
24+
});
1325

1426
describe('configureIpcHandlers', () => {
15-
let mockConversionHandler: jest.Mocked<ConversionHandler>;
27+
let ipcMainMock: any;
28+
let conversionHandlerMock: any;
1629

1730
beforeEach(() => {
18-
jest.resetAllMocks();
19-
mockConversionHandler = new ConversionHandler() as jest.Mocked<ConversionHandler>;
20-
});
31+
ipcMainMock = {
32+
handle: jest.fn(),
33+
};
2134

22-
afterEach(() => {
23-
ipcMain.removeAllListeners();
35+
jest.clearAllMocks();
36+
conversionHandlerMock = new ConversionHandler();
2437
});
2538

26-
test('should handle GET_DESKTOP_PATH', async () => {
27-
const mockGetDesktopPath = jest.mocked(getDesktopPath);
28-
mockGetDesktopPath.mockReturnValue('/mock/desktop/path');
39+
it('should handle GET_DESKTOP_PATH and call getDesktopPath()', () => {
40+
configureIpcHandlers(ipcMainMock);
2941

30-
configureIpcHandlers(ipcMain);
42+
const mockGetDesktopPath = getDesktopPath as jest.Mock;
43+
mockGetDesktopPath.mockReturnValue('/path/to/desktop');
3144

32-
const handler = (ipcMain.handle as jest.Mock).mock.calls.find(
33-
([event]) => event === IpcEvent.GET_DESKTOP_PATH
34-
)[1];
45+
const handler = ipcMainMock.handle.mock.calls.find((call: any) => call[0] === IpcEvent.GET_DESKTOP_PATH)[1];
46+
handler();
3547

36-
const result = await handler();
37-
38-
expect(result).toBe('/mock/desktop/path');
3948
expect(mockGetDesktopPath).toHaveBeenCalled();
4049
});
4150

42-
test('should handle DIALOG_SELECT_DIRECTORY', async () => {
43-
const mockShowOpenDialog = jest.mocked(dialog.showOpenDialog);
44-
mockShowOpenDialog.mockResolvedValue({ canceled: false, filePaths: ['/mock/directory'] });
45-
46-
configureIpcHandlers(ipcMain);
51+
it('should handle DIALOG_SELECT_DIRECTORY and call dialog.showOpenDialog()', async () => {
52+
configureIpcHandlers(ipcMainMock);
4753

48-
const handler = (ipcMain.handle as jest.Mock).mock.calls.find(
49-
([event]) => event === IpcEvent.DIALOG_SELECT_DIRECTORY
50-
)[1];
54+
const dialogMock = dialog.showOpenDialog as jest.Mock;
55+
dialogMock.mockResolvedValue({ canceled: false, filePaths: ['/path/to/directory'] });
5156

57+
const handler = ipcMainMock.handle.mock.calls.find((call: any) => call[0] === IpcEvent.DIALOG_SELECT_DIRECTORY)[1];
5258
const result = await handler();
5359

54-
expect(result).toBe('/mock/directory');
55-
expect(mockShowOpenDialog).toHaveBeenCalledWith({
56-
properties: ['openDirectory'],
57-
});
60+
expect(dialogMock).toHaveBeenCalledWith({ properties: ['openDirectory'] });
61+
expect(result).toBe('/path/to/directory');
5862
});
5963

60-
test('should handle DIALOG_SELECT_DIRECTORY cancellation', async () => {
61-
const mockShowOpenDialog = jest.mocked(dialog.showOpenDialog);
62-
mockShowOpenDialog.mockResolvedValue({ canceled: true, filePaths: [] });
64+
it('should return null if directory selection is canceled', async () => {
65+
configureIpcHandlers(ipcMainMock);
6366

64-
configureIpcHandlers(ipcMain);
65-
66-
const handler = (ipcMain.handle as jest.Mock).mock.calls.find(
67-
([event]) => event === IpcEvent.DIALOG_SELECT_DIRECTORY
68-
)[1];
67+
const dialogMock = dialog.showOpenDialog as jest.Mock;
68+
dialogMock.mockResolvedValue({ canceled: true, filePaths: [] });
6969

70+
const handler = ipcMainMock.handle.mock.calls.find((call: any) => call[0] === IpcEvent.DIALOG_SELECT_DIRECTORY)[1];
7071
const result = await handler();
7172

73+
expect(dialogMock).toHaveBeenCalledWith({ properties: ['openDirectory'] });
7274
expect(result).toBeNull();
73-
expect(mockShowOpenDialog).toHaveBeenCalledWith({
74-
properties: ['openDirectory'],
75-
});
7675
});
7776

78-
test('should handle CONVERT_MEDIA', async () => {
79-
// Make sure the mocked conversion handler returns a resolved promise
80-
mockConversionHandler.handle.mockResolvedValue('/mock/output/path');
81-
82-
configureIpcHandlers(ipcMain);
83-
84-
const handler = (ipcMain.handle as jest.Mock).mock.calls.find(
85-
([event]) => event === IpcEvent.CONVERT_MEDIA
86-
)[1];
87-
88-
const result = await handler(
89-
{} as IpcMainInvokeEvent,
90-
{
91-
id: '1',
92-
filePath: '/mock/path/video.mp4',
93-
outputFormat: 'mp4',
94-
saveDirectory: '/mock/save',
95-
mediaType: 'video',
96-
}
97-
);
77+
it('should handle CONVERT_MEDIA and call conversionHandler.handle()', async () => {
78+
configureIpcHandlers(ipcMainMock);
9879

99-
expect(result).toBe('/mock/output/path');
100-
expect(mockConversionHandler.handle).toHaveBeenCalledWith(
80+
const mockHandle = conversionHandlerMock.handle as jest.Mock;
81+
mockHandle.mockResolvedValue('conversion-success');
82+
83+
const handler = ipcMainMock.handle.mock.calls.find((call: any) => call[0] === IpcEvent.CONVERT_MEDIA)[1];
84+
85+
console.log(handler);
86+
87+
88+
const mockEvent = {} as IpcMainInvokeEvent;
89+
const mediaParams = {
90+
id: '1',
91+
filePath: '/path/to/file',
92+
outputFormat: 'mp4',
93+
saveDirectory: '/path/to/save',
94+
mediaType: 'video',
95+
};
96+
97+
const result = await handler(mockEvent, mediaParams);
98+
99+
expect(mockHandle).toHaveBeenCalledWith(
101100
'1',
102-
'/mock/path/video.mp4',
101+
'/path/to/file',
103102
'mp4',
104-
'/mock/save',
103+
'/path/to/save',
105104
'video',
106-
expect.any(Object)
105+
mockEvent
107106
);
107+
expect(result).toBe('conversion-success');
108108
});
109109

110-
test('should handle CANCEL_ITEM_CONVERSION', () => {
111-
// Mock the cancel method to return true
112-
mockConversionHandler.cancel.mockReturnValue(true);
110+
it('should handle CANCEL_CONVERSION and call conversionHandler.cancelAll()', () => {
111+
configureIpcHandlers(ipcMainMock);
113112

114-
configureIpcHandlers(ipcMain);
113+
const mockCancelAll = conversionHandlerMock.cancelAll as jest.Mock;
115114

116-
const handler = (ipcMain.handle as jest.Mock).mock.calls.find(
117-
([event]) => event === IpcEvent.CANCEL_ITEM_CONVERSION
118-
)[1];
115+
const handler = ipcMainMock.handle.mock.calls.find((call: any) => call[0] === IpcEvent.CANCEL_CONVERSION)[1];
116+
const result = handler();
119117

120-
const result = handler({} as IpcMainInvokeEvent, '1');
121-
122-
expect(result).toBe(true);
123-
expect(mockConversionHandler.cancel).toHaveBeenCalledWith('1');
118+
expect(mockCancelAll).toHaveBeenCalled();
119+
expect(result).toBe('cancel-all-success');
124120
});
125121

126-
test('should handle CANCEL_CONVERSION', () => {
127-
// Ensure cancelAll doesn't return undefined but behaves as expected
128-
mockConversionHandler.cancelAll.mockImplementation(() => true);
129-
130-
configureIpcHandlers(ipcMain);
122+
it('should handle CANCEL_ITEM_CONVERSION and call conversionHandler.cancel()', () => {
123+
configureIpcHandlers(ipcMainMock);
131124

132-
const handler = (ipcMain.handle as jest.Mock).mock.calls.find(
133-
([event]) => event === IpcEvent.CANCEL_CONVERSION
134-
)[1];
125+
const mockCancel = conversionHandlerMock.cancel as jest.Mock;
135126

136-
const result = handler({} as IpcMainInvokeEvent);
127+
const handler = ipcMainMock.handle.mock.calls.find((call: any) => call[0] === IpcEvent.CANCEL_ITEM_CONVERSION)[1];
128+
const result = handler({}, '1');
137129

138-
expect(result).toBe(true);
139-
expect(mockConversionHandler.cancelAll).toHaveBeenCalled();
130+
expect(mockCancel).toHaveBeenCalledWith('1');
131+
expect(result).toBe('cancel-item-success');
140132
});
141133
});

0 commit comments

Comments
 (0)