fix(build): resolve dependencies, fix Vitest 4 mocking, and handle file existence

This commit is contained in:
Tianyao 2026-04-02 19:50:43 +08:00
parent 8ea6edf51e
commit 6161bd75cd
5 changed files with 1440 additions and 1327 deletions

2710
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -17,8 +17,8 @@
"license": "0-BSD",
"devDependencies": {
"@eslint/js": "9.30.1",
"@eslint/json": "^1.2.0",
"@types/node": "^16.11.6",
"@eslint/json": "0.14.0",
"@types/node": "^24.0.0",
"@vitest/ui": "^4.1.2",
"esbuild": "0.25.5",
"eslint-plugin-obsidianmd": "0.1.9",
@ -29,7 +29,7 @@
"tslib": "2.4.0",
"typescript": "^5.8.3",
"typescript-eslint": "8.35.1",
"vitest": "^1.6.1"
"vitest": "^4.1.2"
},
"dependencies": {
"obsidian": "latest"

View file

@ -16,6 +16,10 @@ export class SyncManager {
}
async pushFile(file: TFile) {
if (!this.app.vault.getFileByPath(file.path)) {
new Notice(`File ${file.name} no longer exists in vault.`);
return;
}
const content = await this.app.vault.read(file);
try {
// Conflict detection
@ -62,6 +66,10 @@ export class SyncManager {
}
async pullFile(file: TFile) {
if (!this.app.vault.getFileByPath(file.path)) {
new Notice(`File ${file.name} no longer exists in vault.`);
return;
}
try {
const remote = await this.gitlab.getFile(file.path, this.settings.branch);
const localContent = await this.app.vault.read(file);

View file

@ -14,6 +14,7 @@ const mockApp = {
vault: {
read: vi.fn(),
modify: vi.fn(),
getFileByPath: vi.fn(),
}
} as unknown as App;
@ -36,6 +37,8 @@ describe('SyncManager', () => {
beforeEach(() => {
vi.clearAllMocks();
// Default: file exists in vault
vi.spyOn(mockApp.vault, 'getFileByPath').mockReturnValue(new TFile());
manager = new SyncManager(mockApp, mockGitLab, mockSettings);
});
@ -90,7 +93,7 @@ describe('SyncManager', () => {
// Capture the callback passed to the modal
let callback: (choice: 'local' | 'remote') => void = () => {};
modalMock.mockImplementation((app, file, local, remote, onChoose) => {
modalMock.mockImplementation(function(app, file, local, remote, onChoose) {
callback = onChoose;
return {
open: vi.fn(),
@ -130,7 +133,7 @@ describe('SyncManager', () => {
const modalMock = vi.mocked(SyncConflictModal);
let callback: (choice: 'local' | 'remote') => void = () => {};
modalMock.mockImplementation((app, file, local, remote, onChoose) => {
modalMock.mockImplementation(function(app, file, local, remote, onChoose) {
callback = onChoose;
return {
open: vi.fn(),
@ -190,4 +193,35 @@ describe('SyncManager', () => {
expect(getSpy).toHaveBeenCalled();
expect(mockSettings.syncMetadata['test.md']?.lastSyncedSha).toBe('sha');
});
it('should handle file not existing in vault', async () => {
const mockFile = Object.assign(new TFile(), { path: 'non-existent.md', name: 'non-existent.md' });
vi.spyOn(mockApp.vault, 'getFileByPath').mockReturnValue(null);
await manager.pushFile(mockFile);
expect(mockGitLab.getFile).not.toHaveBeenCalled();
expect(mockGitLab.pushFile).not.toHaveBeenCalled();
});
it('should add new file to repo when it exists locally but not on remote', async () => {
const mockFile = Object.assign(new TFile(), { path: 'new.md', name: 'new.md' });
mockSettings.syncMetadata = {};
vi.spyOn(mockApp.vault, 'read').mockResolvedValue('new local content');
// Remote returns 404/empty
vi.spyOn(mockGitLab, 'getFile').mockResolvedValueOnce({ content: '', sha: '' });
vi.spyOn(mockGitLab, 'pushFile').mockResolvedValue('new.md');
vi.spyOn(mockGitLab, 'getFile').mockResolvedValue({ content: 'new local content', sha: 'new-sha' });
await manager.pushFile(mockFile);
expect(mockGitLab.pushFile).toHaveBeenCalledWith(
'new.md',
'new local content',
'main',
expect.stringContaining('Update new.md')
);
expect(mockSettings.syncMetadata['new.md']?.lastSyncedSha).toBe('new-sha');
});
});

View file

@ -27,7 +27,9 @@ export const Setting = class {
addToggle(_cb: unknown) { return this; }
addButton(_cb: unknown) { return this; }
};
export const Notice = vi.fn().mockImplementation((message: string) => ({}));
export const Notice = class {
constructor(message: string) {}
};
export const Modal = class {
constructor(_app: unknown) {}
open() {}
@ -43,6 +45,7 @@ export const App = class {
vault = {
read: vi.fn(),
modify: vi.fn(),
getFileByPath: vi.fn(),
on: vi.fn(),
adapter: {
getBasePath: vi.fn().mockReturnValue('/mock/path'),