firstsun-dev_git-files-sync/tests/logic/sync-manager-mapping.test.ts
ClaudiaFang 28f4f8efd0 feat: resize conflict modal, add connection status badge, and local ignore patterns
- Conflict modal (#42): resize to min(1100px,92vw) x min(85vh,800px) flex
  layout, remove the 280px content height cap, and add a Diff/Local/Remote
  tab switcher on narrow screens (defaults to Diff).
- Settings connection status (#41): show a persistent Connected/Not
  connected/Checking badge in the settings tab, auto-tested on open and
  after an 800ms debounce on token/branch/URL/owner/repo edits, updated
  in place (no full re-render, so typing focus is preserved).
- Local ignore patterns (#40): new "Ignore patterns" setting (.gitignore-
  style, multi-line) applied in GitignoreManager.isIgnored() in addition
  to the repo's own .gitignore, covering push/pull/refresh uniformly.

Closes #42, #41, #40.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YYCTyZw7gUmJ7oh1VTmAqh
2026-07-13 12:19:17 +00:00

124 lines
3.8 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { SyncManager } from '../../src/logic/sync-manager';
import { App, TFile } from 'obsidian';
import type { GitServiceInterface } from '../../src/services/git-service-interface';
import type { GitLabFilesPushSettings } from '../../src/settings';
vi.mock('obsidian', () => ({
Notice: vi.fn(),
TFile: class {
path: string = '';
name: string = '';
},
App: class {},
}));
const mockApp = {
vault: {
read: vi.fn(),
modify: vi.fn(),
getFileByPath: vi.fn(),
getAbstractFileByPath: vi.fn(),
createFolder: vi.fn(),
adapter: {
exists: vi.fn(),
read: vi.fn(),
write: vi.fn(),
}
}
} as unknown as App;
const mockGetFile = vi.fn();
const mockPushFile = vi.fn();
const mockGitService = {
getFile: mockGetFile,
pushFile: mockPushFile,
} as unknown as GitServiceInterface;
const mockSettings: GitLabFilesPushSettings = {
serviceType: 'gitlab',
gitlabToken: '',
gitlabBaseUrl: 'https://gitlab.com',
projectId: '',
githubToken: '',
githubOwner: '',
githubRepo: '',
giteaToken: '',
giteaBaseUrl: '',
giteaOwner: '',
giteaRepo: '',
branch: 'main',
rootPath: 'notes',
vaultFolder: 'Work',
syncMetadata: {},
symlinkHandling: 'real',
ignorePatterns: '',
};
describe('SyncManager Mapping', () => {
let manager: SyncManager;
beforeEach(() => {
vi.clearAllMocks();
mockSettings.syncMetadata = {};
manager = new SyncManager(mockApp, mockGitService, mockSettings);
});
it('should strip vaultFolder when pushing', async () => {
const vaultPath = 'Work/test.md';
const mockFile = Object.assign(new TFile(), { path: vaultPath, name: 'test.md' });
const getFileByPathSpy = vi.spyOn(mockApp.vault, 'getFileByPath');
const readSpy = vi.spyOn(mockApp.vault, 'read');
getFileByPathSpy.mockReturnValue(mockFile);
readSpy.mockResolvedValue('content');
vi.mocked(mockGetFile).mockResolvedValue({ content: '', sha: '' });
vi.mocked(mockPushFile).mockResolvedValue({ path: 'notes/test.md' });
await manager.pushFile(mockFile);
expect(mockGetFile).toHaveBeenCalledWith('test.md', 'main');
expect(mockPushFile).toHaveBeenCalledWith(
'test.md',
'content',
'main',
'Update test.md from Obsidian',
''
);
});
it('should map back to vaultFolder when pulling', async () => {
const vaultPath = 'Work/remote.md';
vi.mocked(mockGetFile).mockResolvedValue({ content: 'remote content', sha: 'sha' });
const existsSpy = vi.spyOn(mockApp.vault.adapter, 'exists');
const writeSpy = vi.spyOn(mockApp.vault.adapter, 'write');
existsSpy.mockResolvedValue(false);
writeSpy.mockResolvedValue(undefined);
await manager.pullFile(vaultPath);
expect(mockGetFile).toHaveBeenCalledWith('remote.md', 'main');
expect(writeSpy).toHaveBeenCalledWith(vaultPath, 'remote content');
});
it('should handle root-level files correctly when no vaultFolder', async () => {
mockSettings.vaultFolder = '';
manager = new SyncManager(mockApp, mockGitService, mockSettings);
const path = 'root.md';
const mockFile = Object.assign(new TFile(), { path, name: 'root.md' });
const getFileByPathSpy = vi.spyOn(mockApp.vault, 'getFileByPath');
const readSpy = vi.spyOn(mockApp.vault, 'read');
getFileByPathSpy.mockReturnValue(mockFile);
readSpy.mockResolvedValue('content');
vi.mocked(mockGetFile).mockResolvedValue({ content: '', sha: '' });
await manager.pushFile(mockFile);
expect(mockGetFile).toHaveBeenCalledWith('root.md', 'main');
});
});