style: run prettier formatter over src and tests

This commit is contained in:
2026-06-11 20:46:00 +05:30
parent 2389d4f297
commit 6b564a4569
211 changed files with 6134 additions and 4234 deletions
+13 -2
View File
@@ -18,7 +18,12 @@ vi.mock('@/lib/ipc', () => ({
},
app: { getVersion: vi.fn().mockResolvedValue({ ok: true, data: '5.0.1' }) },
menu: { on: vi.fn(() => () => {}) },
updater: { check: vi.fn(), install: vi.fn(), getState: vi.fn(), onStatus: vi.fn(() => () => {}) },
updater: {
check: vi.fn(),
install: vi.fn(),
getState: vi.fn(),
onStatus: vi.fn(() => () => {}),
},
crash: { read: vi.fn(), openDir: vi.fn(), delete: vi.fn() },
},
}));
@@ -35,7 +40,13 @@ describe('App — print preview event listeners', () => {
beforeEach(() => {
useCommandStore.setState({ handlers: {} } as any);
useAppStore.setState({ modal: { kind: null } } as any);
useFileStore.setState({ tree: null, rootPath: null, expanded: new Set(), openTabs: [], activeTabId: null });
useFileStore.setState({
tree: null,
rootPath: null,
expanded: new Set(),
openTabs: [],
activeTabId: null,
});
useSettingsStore.getState().resetToDefaults?.();
useEditorStore.setState({ buffers: new Map(), activeId: null });
localStorage.clear();
+27 -7
View File
@@ -10,8 +10,13 @@ describe('modal commands', () => {
localStorage.clear();
useCommandStore.setState({ handlers: {}, userBindings: {} } as any);
useAppStore.setState({ modal: { kind: null } } as any);
useFileStore.setState({ activeTabId: '/x.md', openTabs: [{ id: '/x.md', path: '/x.md', title: 'x.md', dirty: false }] } as any);
useEditorStore.setState({ buffers: new Map([['/x.md', { id: '/x.md', path: '/x.md', content: '# hi', dirty: false }]]) } as any);
useFileStore.setState({
activeTabId: '/x.md',
openTabs: [{ id: '/x.md', path: '/x.md', title: 'x.md', dirty: false }],
} as any);
useEditorStore.setState({
buffers: new Map([['/x.md', { id: '/x.md', path: '/x.md', content: '# hi', dirty: false }]]),
} as any);
});
it('settings.open opens settings modal', () => {
@@ -35,19 +40,34 @@ describe('modal commands', () => {
it('file.exportPdf opens export-pdf modal with active path', () => {
registerMenuCommands();
useCommandStore.getState().dispatch('file.exportPdf');
expect(useAppStore.getState().modal).toEqual({ kind: 'export-pdf', props: { sourcePath: '/x.md' } });
expect(useAppStore.getState().modal).toEqual({
kind: 'export-pdf',
props: { sourcePath: '/x.md' },
});
});
it('file.exportDocx opens export-docx modal', () => {
registerMenuCommands();
useCommandStore.getState().dispatch('file.exportDocx');
expect(useAppStore.getState().modal).toEqual({ kind: 'export-docx', props: { sourcePath: '/x.md' } });
expect(useAppStore.getState().modal).toEqual({
kind: 'export-docx',
props: { sourcePath: '/x.md' },
});
});
it('file.exportBatch opens export-batch modal with all open files', () => {
useFileStore.setState({ activeTabId: '/x.md', openTabs: [{ id: '/x.md', path: '/x.md', title: 'x.md', dirty: false }, { id: '/y.md', path: '/y.md', title: 'y.md', dirty: false }] } as any);
useFileStore.setState({
activeTabId: '/x.md',
openTabs: [
{ id: '/x.md', path: '/x.md', title: 'x.md', dirty: false },
{ id: '/y.md', path: '/y.md', title: 'y.md', dirty: false },
],
} as any);
registerMenuCommands();
useCommandStore.getState().dispatch('file.exportBatch');
expect(useAppStore.getState().modal).toEqual({ kind: 'export-batch', props: { sourcePaths: ['/x.md', '/y.md'] } });
expect(useAppStore.getState().modal).toEqual({
kind: 'export-batch',
props: { sourcePaths: ['/x.md', '/y.md'] },
});
});
});
});
+9 -3
View File
@@ -10,7 +10,10 @@ describe('Phase 9 commands', () => {
localStorage.clear();
useCommandStore.setState({ handlers: {}, userBindings: {} } as any);
useAppStore.setState({ modal: { kind: null } } as any);
useFileStore.setState({ activeTabId: '/x.md', openTabs: [{ id: '/x.md', path: '/x.md', title: 'x.md', dirty: false }] } as any);
useFileStore.setState({
activeTabId: '/x.md',
openTabs: [{ id: '/x.md', path: '/x.md', title: 'x.md', dirty: false }],
} as any);
// Use resetToDefaults to restore store methods (setSetting/resetToDefaults)
useSettingsStore.getState().resetToDefaults();
});
@@ -36,7 +39,10 @@ describe('Phase 9 commands', () => {
it('tools.exportWord opens export-word modal with active path', () => {
registerMenuCommands();
useCommandStore.getState().dispatch('tools.exportWord');
expect(useAppStore.getState().modal).toEqual({ kind: 'export-word', props: { sourcePath: '/x.md' } });
expect(useAppStore.getState().modal).toEqual({
kind: 'export-word',
props: { sourcePath: '/x.md' },
});
});
it('tools.repl toggles replOpen setting', () => {
@@ -47,4 +53,4 @@ describe('Phase 9 commands', () => {
useCommandStore.getState().dispatch('tools.repl');
expect(useSettingsStore.getState().replOpen).toBe(false);
});
});
});
+3 -1
View File
@@ -13,7 +13,9 @@ describe('useScrollSync', () => {
it('throttles editor scroll events to 60fps', () => {
const onScroll = vi.fn();
const { result } = renderHook(() => useScrollSync({ onEditorScroll: onScroll }));
const mockEvt = { currentTarget: { scrollTop: 100, scrollHeight: 1000, clientHeight: 200 } } as any;
const mockEvt = {
currentTarget: { scrollTop: 100, scrollHeight: 1000, clientHeight: 200 },
} as any;
// All calls within the same act() - performance.now() stays at 0
// First call passes, subsequent calls within FRAME_MS are throttled
act(() => {
+12 -2
View File
@@ -95,7 +95,12 @@ describe('useShortcut', () => {
it('calls preventDefault when the shortcut matches', () => {
const cb = vi.fn();
renderHook(() => useShortcut('mod+s', cb));
const event = new KeyboardEvent('keydown', { key: 's', ctrlKey: true, bubbles: true, cancelable: true });
const event = new KeyboardEvent('keydown', {
key: 's',
ctrlKey: true,
bubbles: true,
cancelable: true,
});
window.dispatchEvent(event);
expect(event.defaultPrevented).toBe(true);
});
@@ -103,7 +108,12 @@ describe('useShortcut', () => {
it('does NOT call preventDefault when the shortcut does NOT match', () => {
const cb = vi.fn();
renderHook(() => useShortcut('mod+s', cb));
const event = new KeyboardEvent('keydown', { key: 'x', ctrlKey: true, bubbles: true, cancelable: true });
const event = new KeyboardEvent('keydown', {
key: 'x',
ctrlKey: true,
bubbles: true,
cancelable: true,
});
window.dispatchEvent(event);
expect(event.defaultPrevented).toBe(false);
});
+2 -2
View File
@@ -39,7 +39,7 @@ describe('toAsciiTable', () => {
expect(out).toContain('| X | 100 |');
// Right-aligned: "YY" padStart(4) -> " YY", "25" padStart(3) -> " 25"
expect(out).toContain('| YY | 25 |');
});
});
});
describe('applyAsciiTransform', () => {
@@ -57,4 +57,4 @@ describe('applyAsciiTransform', () => {
const md = '# Hello\n\nNo tables here.';
expect(applyAsciiTransform(md)).toBe(md);
});
});
});
+4 -2
View File
@@ -6,7 +6,9 @@ describe('generateDocx', () => {
const blob = await generateDocx({ source: '# Hello\n\nWorld' });
expect(blob).toBeInstanceOf(Blob);
expect(blob.size).toBeGreaterThan(0);
expect(blob.type).toBe('application/vnd.openxmlformats-officedocument.wordprocessingml.document');
expect(blob.type).toBe(
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
);
});
it('converts headings to docx heading styles', async () => {
@@ -21,4 +23,4 @@ describe('generateDocx', () => {
expect(blob).toBeInstanceOf(Blob);
expect(blob.size).toBeGreaterThan(0);
});
});
});
+1 -1
View File
@@ -38,4 +38,4 @@ describe('ipc wrapper', () => {
expect(result.error.code).toBe('CHANNEL_MISSING');
}
});
});
});
+1 -1
View File
@@ -27,4 +27,4 @@ describe('renderMarkdown', () => {
const html = renderMarkdown('<script>alert(1)</script>');
expect(html).not.toContain('<script>');
});
});
});
+1 -7
View File
@@ -1,11 +1,5 @@
import { describe, it, expect } from 'vitest';
import {
fadeIn,
slideInRight,
modalPop,
toastSpring,
sidebarToggle,
} from '@/lib/motion';
import { fadeIn, slideInRight, modalPop, toastSpring, sidebarToggle } from '@/lib/motion';
describe('motion presets', () => {
it('fadeIn is a valid transition object', () => {
+11 -12
View File
@@ -3,18 +3,19 @@ import { render, act } from '@testing-library/react';
import { useCommandStore } from '@/stores/command-store';
import { useFileStore } from '@/stores/file-store';
import { useAppStore } from '@/stores/app-store';
import { useRegisterMenuCommands, useBridgeNativeMenu } from '@/lib/commands/register-menu-commands';
import {
useRegisterMenuCommands,
useBridgeNativeMenu,
} from '@/lib/commands/register-menu-commands';
type Cleanup = () => void;
const menuListeners = new Map<string, (...args: unknown[]) => void>();
const menuOn = vi.fn(
(channel: string, callback: (...args: unknown[]) => void): Cleanup => {
menuListeners.set(channel, callback);
return () => {
if (menuListeners.get(channel) === callback) menuListeners.delete(channel);
};
}
);
const menuOn = vi.fn((channel: string, callback: (...args: unknown[]) => void): Cleanup => {
menuListeners.set(channel, callback);
return () => {
if (menuListeners.get(channel) === callback) menuListeners.delete(channel);
};
});
vi.mock('@/lib/ipc', () => ({
ipc: {
@@ -146,9 +147,7 @@ describe('useRegisterMenuCommands + useBridgeNativeMenu', () => {
it('file.clearRecent does NOT clear openTabs', () => {
useFileStore.setState({
openTabs: [
{ id: '/a.md', path: '/a.md', title: 'a.md', dirty: false },
],
openTabs: [{ id: '/a.md', path: '/a.md', title: 'a.md', dirty: false }],
activeTabId: '/a.md',
});
render(<Harness />);
+12 -4
View File
@@ -21,7 +21,9 @@ describe('settingsSchema', () => {
describe('exportPdfSchema', () => {
it('accepts a4 + normal margins + embedFonts true', () => {
expect(exportPdfSchema.safeParse({ format: 'a4', margins: 'normal', embedFonts: true }).success).toBe(true);
expect(
exportPdfSchema.safeParse({ format: 'a4', margins: 'normal', embedFonts: true }).success
).toBe(true);
});
it('rejects unknown format', () => {
expect(exportPdfSchema.safeParse({ format: 'b4' }).success).toBe(false);
@@ -39,15 +41,21 @@ describe('exportDocxSchema', () => {
describe('exportHtmlSchema', () => {
it('accepts github highlight style', () => {
expect(exportHtmlSchema.safeParse({ standalone: true, highlightStyle: 'github' }).success).toBe(true);
expect(exportHtmlSchema.safeParse({ standalone: true, highlightStyle: 'github' }).success).toBe(
true
);
});
});
describe('exportBatchSchema', () => {
it('accepts a non-empty file list', () => {
expect(exportBatchSchema.safeParse({ format: 'pdf', concurrency: 4, filePaths: ['/a.md'] }).success).toBe(true);
expect(
exportBatchSchema.safeParse({ format: 'pdf', concurrency: 4, filePaths: ['/a.md'] }).success
).toBe(true);
});
it('rejects empty file list', () => {
expect(exportBatchSchema.safeParse({ format: 'pdf', concurrency: 4, filePaths: [] }).success).toBe(false);
expect(
exportBatchSchema.safeParse({ format: 'pdf', concurrency: 4, filePaths: [] }).success
).toBe(false);
});
});
@@ -19,9 +19,14 @@ describe('MigrationRunner', () => {
test('runs migration on missing v5 marker, writes v5 file, backs up v4', () => {
const dir = tmpDir();
fs.writeFileSync(path.join(dir, 'settings.json'), JSON.stringify({ theme: 'light' }));
const r = new MigrationRunner({ dir, transform: (v) => ({ ...v, theme: 'system', 'migration.version': 5 }) });
const r = new MigrationRunner({
dir,
transform: (v) => ({ ...v, theme: 'system', 'migration.version': 5 }),
});
expect(r.run()).toBe('migrated');
expect(JSON.parse(fs.readFileSync(path.join(dir, 'settings.json'), 'utf-8'))['migration.version']).toBe(5);
expect(
JSON.parse(fs.readFileSync(path.join(dir, 'settings.json'), 'utf-8'))['migration.version']
).toBe(5);
expect(fs.existsSync(path.join(dir, 'settings.v4.bak.json'))).toBe(true);
fs.rmSync(dir, { recursive: true });
});
@@ -37,16 +42,28 @@ describe('MigrationRunner', () => {
test('returns failed and writes v5 marker so subsequent runs skip migration', () => {
const dir = tmpDir();
fs.writeFileSync(path.join(dir, 'settings.json'), JSON.stringify({ theme: 'light' }));
const r = new MigrationRunner({ dir, transform: () => { throw new Error('boom'); } });
const r = new MigrationRunner({
dir,
transform: () => {
throw new Error('boom');
},
});
expect(r.run()).toBe('failed');
// v5 marker is written so next launch skips migration
expect(JSON.parse(fs.readFileSync(path.join(dir, 'settings.json'), 'utf-8'))['migration.version']).toBe(5);
expect(
JSON.parse(fs.readFileSync(path.join(dir, 'settings.json'), 'utf-8'))['migration.version']
).toBe(5);
// Original data is preserved (theme: light is still there)
expect(fs.readFileSync(path.join(dir, 'settings.json'), 'utf-8')).toContain('light');
// Backup exists
expect(fs.existsSync(path.join(dir, 'settings.v4.bak.json'))).toBe(true);
// Subsequent run skips
const r2 = new MigrationRunner({ dir, transform: () => { throw new Error('should not be called'); } });
const r2 = new MigrationRunner({
dir,
transform: () => {
throw new Error('should not be called');
},
});
expect(r2.run()).toBe('skipped');
fs.rmSync(dir, { recursive: true });
});
@@ -30,10 +30,7 @@ describe('UpdaterService', () => {
emitter.emit('update-available', { version: '5.0.2' });
});
await service.check();
expect(states).toEqual([
{ state: 'checking' },
{ state: 'available', version: '5.0.2' },
]);
expect(states).toEqual([{ state: 'checking' }, { state: 'available', version: '5.0.2' }]);
});
test('check() emits error on network failure', async () => {
@@ -57,4 +54,4 @@ describe('UpdaterService', () => {
service.install();
expect(mockAutoUpdater.quitAndInstall).toHaveBeenCalled();
});
});
});
+4 -1
View File
@@ -53,7 +53,10 @@ describe('useAppStore (modal)', () => {
it('openModal with kind requiring props passes them through', () => {
useAppStore.getState().openModal('export-pdf', { sourcePath: '/a.md' });
expect(useAppStore.getState().modal).toEqual({ kind: 'export-pdf', props: { sourcePath: '/a.md' } });
expect(useAppStore.getState().modal).toEqual({
kind: 'export-pdf',
props: { sourcePath: '/a.md' },
});
});
it('closeModal clears the modal state', () => {
+1 -1
View File
@@ -44,4 +44,4 @@ describe('useEditorStore', () => {
expect(newBuf?.path).toBe('/bar.md');
expect(useEditorStore.getState().activeId).toBe('file-2');
});
});
});
+15 -5
View File
@@ -54,7 +54,9 @@ describe('file-store toasts', () => {
openTabs: [{ id: '/test.md', path: '/test.md', title: 'test.md', dirty: false }],
} as any);
useEditorStore.setState({
buffers: new Map([['/test.md', { id: '/test.md', path: '/test.md', content: '# hi', dirty: true }]]),
buffers: new Map([
['/test.md', { id: '/test.md', path: '/test.md', content: '# hi', dirty: true }],
]),
activeId: '/test.md',
} as any);
@@ -64,13 +66,18 @@ describe('file-store toasts', () => {
});
it('calls toast.error when save fails', async () => {
fakeWrite.mockResolvedValue({ ok: false, error: { code: 'EACCES', message: 'Permission denied' } });
fakeWrite.mockResolvedValue({
ok: false,
error: { code: 'EACCES', message: 'Permission denied' },
});
useFileStore.setState({
activeTabId: '/test.md',
openTabs: [{ id: '/test.md', path: '/test.md', title: 'test.md', dirty: false }],
} as any);
useEditorStore.setState({
buffers: new Map([['/test.md', { id: '/test.md', path: '/test.md', content: '# hi', dirty: true }]]),
buffers: new Map([
['/test.md', { id: '/test.md', path: '/test.md', content: '# hi', dirty: true }],
]),
activeId: '/test.md',
} as any);
@@ -91,10 +98,13 @@ describe('file-store toasts', () => {
describe('openFolder', () => {
it('calls toast.error when IPC list fails', async () => {
fakeList.mockResolvedValue({ ok: false, error: { code: 'EACCES', message: 'Permission denied' } });
fakeList.mockResolvedValue({
ok: false,
error: { code: 'EACCES', message: 'Permission denied' },
});
await useFileStore.getState().openFolder('/forbidden');
expect(toast.error).toHaveBeenCalledWith('Failed to open folder: Permission denied');
});
});
});
});
+4 -5
View File
@@ -49,10 +49,7 @@ describe('useFileStore', () => {
it('openFolder calls ipc.file.list and sets tree with root node and mapped children', async () => {
fakeList.mockResolvedValue({
ok: true,
data: [
fakeFileEntry('README.md', false),
fakeFileEntry('src', true),
],
data: [fakeFileEntry('README.md', false), fakeFileEntry('src', true)],
});
await useFileStore.getState().openFolder('/root');
@@ -139,7 +136,9 @@ describe('useFileStore', () => {
path: '/root',
isDirectory: true,
loaded: true,
children: [{ name: 'src', path: '/root/src', isDirectory: true, children: null, loaded: true }],
children: [
{ name: 'src', path: '/root/src', isDirectory: true, children: null, loaded: true },
],
};
useFileStore.setState({ tree });
+1 -1
View File
@@ -41,4 +41,4 @@ describe('useSettingsStore', () => {
expect(parsed.state.setSetting).toBeUndefined();
expect(parsed.state.resetToDefaults).toBeUndefined();
});
});
});