firstsun-dev_git-files-sync/tests/ui/SettingsConnectionStatus.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

95 lines
3.5 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-deprecated -- exercising the Obsidian < 1.13 display() compat fallback intentionally */
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { App } from 'obsidian';
import { DEFAULT_SETTINGS, GitLabSyncSettingTab } from '../../src/settings';
import GitLabFilesPush from '../../src/main';
import { createContainer, setupObsidianDOM } from './setup-dom';
vi.mock('../../src/main', () => ({
default: class {},
}));
beforeAll(() => { setupObsidianDOM(); });
function createPluginStub(testConnection: ReturnType<typeof vi.fn>): GitLabFilesPush {
return {
settings: { ...DEFAULT_SETTINGS },
saveSettings: vi.fn().mockResolvedValue(undefined),
initializeGitService: vi.fn(),
gitService: { testConnection },
} as unknown as GitLabFilesPush;
}
function scheduleConnectionTest(tab: GitLabSyncSettingTab): void {
(tab as unknown as { scheduleConnectionTest: () => void }).scheduleConnectionTest();
}
describe('GitLabSyncSettingTab connection status badge', () => {
beforeEach(() => {
vi.useFakeTimers();
});
it('shows checking then connected after opening the tab', async () => {
const testConnection = vi.fn().mockResolvedValue({ repoOk: true, branchOk: true });
const tab = new GitLabSyncSettingTab(new App(), createPluginStub(testConnection));
tab.containerEl = createContainer();
tab.display();
const badge = tab.containerEl.querySelector('.gfs-connection-status') as HTMLElement;
expect(badge.classList.contains('is-checking')).toBe(true);
await vi.advanceTimersByTimeAsync(800);
expect(testConnection).toHaveBeenCalledTimes(1);
expect(badge.classList.contains('is-connected')).toBe(true);
expect(badge.textContent).toBe('Connected');
vi.useRealTimers();
});
it('debounces repeated field edits into a single connection test', async () => {
const testConnection = vi.fn().mockResolvedValue({ repoOk: false, branchOk: false, error: 'bad token' });
const plugin = createPluginStub(testConnection);
const tab = new GitLabSyncSettingTab(new App(), plugin);
tab.containerEl = createContainer();
tab.display();
await vi.advanceTimersByTimeAsync(800);
testConnection.mockClear();
// Simulate rapid keystrokes in the token field via the debounced hook directly.
scheduleConnectionTest(tab);
scheduleConnectionTest(tab);
scheduleConnectionTest(tab);
await vi.advanceTimersByTimeAsync(799);
expect(testConnection).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(1);
expect(testConnection).toHaveBeenCalledTimes(1);
const badge = tab.containerEl.querySelector('.gfs-connection-status') as HTMLElement;
expect(badge.classList.contains('is-disconnected')).toBe(true);
expect(badge.textContent).toContain('bad token');
vi.useRealTimers();
});
});
describe('GitLabSyncSettingTab ignore patterns setting', () => {
it('renders a textarea seeded with the saved ignorePatterns value', async () => {
const plugin = createPluginStub(vi.fn().mockResolvedValue({ repoOk: true, branchOk: true }));
plugin.settings.ignorePatterns = 'draft/\n*.tmp';
const tab = new GitLabSyncSettingTab(new App(), plugin);
tab.containerEl = createContainer();
vi.useFakeTimers();
tab.display();
await vi.advanceTimersByTimeAsync(800);
vi.useRealTimers();
const textarea = tab.containerEl.querySelector('textarea') as HTMLTextAreaElement;
expect(textarea.value).toBe('draft/\n*.tmp');
});
});