chore: phase 2 and 3 refactors

This commit is contained in:
2026-02-21 18:02:20 +01:00
parent 40892b9302
commit 87200a8ad9
24 changed files with 411 additions and 74 deletions

View File

@@ -142,7 +142,7 @@ describe('Pages shortcut UI', () => {
expect(window.electronAPI.posts.filter).not.toHaveBeenCalledWith({ categories: ['page'] });
});
it('uses a flex-height wrapper for active posts/pages sidebar view', async () => {
it('conditionally mounts only the active posts/pages sidebar content', async () => {
useAppStore.setState({
activeView: 'posts',
sidebarVisible: true,
@@ -153,8 +153,7 @@ describe('Pages shortcut UI', () => {
expect(await screen.findByText('POSTS')).toBeInTheDocument();
const wrappers = container.querySelectorAll('.sidebar > div');
expect(wrappers.length).toBeGreaterThanOrEqual(2);
expect((wrappers[0] as HTMLElement).style.display).toBe('flex');
expect(wrappers.length).toBe(1);
});
it('opens style tab from settings sidebar navigation', async () => {

View File

@@ -51,4 +51,16 @@ describe('Help menu documentation entry', () => {
it('maps Validate Site to a renderer menu event', () => {
expect(APP_MENU_ACTION_EVENT_MAP.validateSite).toBe('menu:validateSite');
});
it('includes Edit Preferences action in Edit menu with comma shortcut', () => {
const editGroup = APP_MENU_GROUPS.find((group) => group.label === 'Edit');
const preferencesItem = editGroup?.items.find((item) => item.action === 'editPreferences');
expect(preferencesItem).toBeDefined();
expect(preferencesItem?.accelerator).toContain(',');
});
it('maps Edit Preferences to a renderer menu event', () => {
expect(APP_MENU_ACTION_EVENT_MAP.editPreferences).toBe('menu:editPreferences');
});
});

View File

@@ -0,0 +1,40 @@
import { describe, expect, it, vi } from 'vitest';
import { activateSidebarSection } from '../../../src/renderer/navigation/sectionActivation';
describe('sectionActivation', () => {
it('opens editor first and delays section activation when editor is not active', () => {
const ensureEditor = vi.fn();
const activateSection = vi.fn();
const schedule = vi.fn((callback: () => void) => callback());
activateSidebarSection({
isEditorTabActive: false,
ensureEditorTabActive: ensureEditor,
activateSection,
schedule,
delayWhenOpeningEditorMs: 100,
});
expect(ensureEditor).toHaveBeenCalledTimes(1);
expect(schedule).toHaveBeenCalledTimes(1);
expect(schedule).toHaveBeenCalledWith(expect.any(Function), 100);
expect(activateSection).toHaveBeenCalledTimes(1);
});
it('activates section immediately when editor tab is already active', () => {
const ensureEditor = vi.fn();
const activateSection = vi.fn();
const schedule = vi.fn((callback: () => void) => callback());
activateSidebarSection({
isEditorTabActive: true,
ensureEditorTabActive: ensureEditor,
activateSection,
schedule,
});
expect(ensureEditor).not.toHaveBeenCalled();
expect(schedule).toHaveBeenCalledWith(expect.any(Function), 0);
expect(activateSection).toHaveBeenCalledTimes(1);
});
});

View File

@@ -0,0 +1,14 @@
import { describe, expect, it } from 'vitest';
import { getPersistedSidebarSection, setPersistedSidebarSection } from '../../../src/renderer/navigation/sidebarUiPersistence';
describe('sidebarUiPersistence', () => {
it('persists and reads section ids by sidebar key', () => {
setPersistedSidebarSection('settings', 'project');
expect(getPersistedSidebarSection('settings')).toBe('project');
});
it('returns null for missing persisted section', () => {
expect(getPersistedSidebarSection('tags')).toBeNull();
});
});

View File

@@ -0,0 +1,30 @@
import { describe, expect, it } from 'vitest';
import {
DEFAULT_SIDEBAR_VIEW,
SIDEBAR_VIEW_REGISTRY,
isSidebarView,
} from '../../../src/renderer/navigation/sidebarViewRegistry';
describe('sidebarViewRegistry', () => {
it('defines all supported sidebar views in one canonical registry', () => {
expect(SIDEBAR_VIEW_REGISTRY).toEqual([
'posts',
'pages',
'media',
'settings',
'tags',
'chat',
'import',
'git',
]);
});
it('uses posts as default sidebar view', () => {
expect(DEFAULT_SIDEBAR_VIEW).toBe('posts');
});
it('validates sidebar view values', () => {
expect(isSidebarView('tags')).toBe(true);
expect(isSidebarView('unknown')).toBe(false);
});
});

View File

@@ -0,0 +1,27 @@
import { describe, expect, it } from 'vitest';
import type { SiteValidationReport } from '../../../src/main/shared/electronApi';
import {
getPersistedSiteValidationReport,
persistSiteValidationReport,
} from '../../../src/renderer/navigation/siteValidationPersistence';
const report: SiteValidationReport = {
sitemapPath: '/tmp/sitemap.xml',
sitemapChanged: false,
missingUrlPaths: ['/foo'],
extraUrlPaths: ['/bar'],
expectedUrlCount: 10,
existingHtmlUrlCount: 9,
};
describe('siteValidationPersistence', () => {
it('persists and loads site validation report by project', () => {
persistSiteValidationReport('project-1', report);
expect(getPersistedSiteValidationReport('project-1')).toEqual(report);
});
it('returns null when project has no persisted report', () => {
expect(getPersistedSiteValidationReport('missing-project')).toBeNull();
});
});

View File

@@ -0,0 +1,25 @@
import { describe, expect, it } from 'vitest';
import { getSingletonToolTabSpec, openSingletonToolTab } from '../../../src/renderer/navigation/tabPolicy';
describe('tabPolicy', () => {
it('provides canonical singleton tab specs', () => {
expect(getSingletonToolTabSpec('settings')).toEqual({ type: 'settings', id: 'settings', isTransient: false });
expect(getSingletonToolTabSpec('tags')).toEqual({ type: 'tags', id: 'tags', isTransient: false });
expect(getSingletonToolTabSpec('style')).toEqual({ type: 'style', id: 'style', isTransient: false });
expect(getSingletonToolTabSpec('documentation')).toEqual({ type: 'documentation', id: 'documentation', isTransient: false });
expect(getSingletonToolTabSpec('metadata-diff')).toEqual({ type: 'metadata-diff', id: 'metadata-diff', isTransient: false });
expect(getSingletonToolTabSpec('site-validation')).toEqual({ type: 'site-validation', id: 'site-validation', isTransient: false });
});
it('opens singleton tool tabs using canonical tab spec', () => {
const openTab = (tab: { type: string; id: string; isTransient: boolean }) => {
captured = tab;
};
let captured: { type: string; id: string; isTransient: boolean } | null = null;
openSingletonToolTab(openTab, 'site-validation');
expect(captured).toEqual({ type: 'site-validation', id: 'site-validation', isTransient: false });
});
});