import { render, screen, fireEvent } from '@testing-library/react' import { describe, it, expect, vi } from 'vitest' import { Sidebar } from './Sidebar' import type { VaultEntry, SidebarSelection } from '../types' const mockEntries: VaultEntry[] = [ { path: '/vault/project/build-app.md', filename: 'build-app.md', title: 'Build Laputa App', isA: 'Project', aliases: [], belongsTo: [], relatedTo: [], status: 'Active', owner: 'Luca', cadence: null, modifiedAt: 1700000000, createdAt: null, fileSize: 1024, snippet: '', relationships: {}, }, { path: '/vault/responsibility/grow-newsletter.md', filename: 'grow-newsletter.md', title: 'Grow Newsletter', isA: 'Responsibility', aliases: [], belongsTo: [], relatedTo: [], status: 'Active', owner: 'Luca', cadence: null, modifiedAt: 1700000000, createdAt: null, fileSize: 512, snippet: '', relationships: {}, }, { path: '/vault/experiment/stock-screener.md', filename: 'stock-screener.md', title: 'Stock Screener', isA: 'Experiment', aliases: [], belongsTo: [], relatedTo: [], status: 'Active', owner: 'Luca', cadence: null, modifiedAt: 1700000000, createdAt: null, fileSize: 256, snippet: '', relationships: {}, }, { path: '/vault/procedure/weekly-essays.md', filename: 'weekly-essays.md', title: 'Write Weekly Essays', isA: 'Procedure', aliases: [], belongsTo: [], relatedTo: [], status: 'Active', owner: 'Luca', cadence: 'Weekly', modifiedAt: 1700000000, createdAt: null, fileSize: 128, snippet: '', relationships: {}, }, { path: '/vault/topic/software-development.md', filename: 'software-development.md', title: 'Software Development', isA: 'Topic', aliases: ['Dev', 'Coding'], belongsTo: [], relatedTo: [], status: null, owner: null, cadence: null, modifiedAt: 1700000000, createdAt: null, fileSize: 256, snippet: '', relationships: {}, }, { path: '/vault/topic/trading.md', filename: 'trading.md', title: 'Trading', isA: 'Topic', aliases: ['Algorithmic Trading'], belongsTo: [], relatedTo: [], status: null, owner: null, cadence: null, modifiedAt: 1700000000, createdAt: null, fileSize: 180, snippet: '', relationships: {}, }, { path: '/vault/person/alice.md', filename: 'alice.md', title: 'Alice', isA: 'Person', aliases: [], belongsTo: [], relatedTo: [], status: null, owner: null, cadence: null, modifiedAt: 1700000000, createdAt: null, fileSize: 100, snippet: '', relationships: {}, }, { path: '/vault/event/kickoff.md', filename: 'kickoff.md', title: 'Kickoff Meeting', isA: 'Event', aliases: [], belongsTo: [], relatedTo: [], status: null, owner: null, cadence: null, modifiedAt: 1700000000, createdAt: null, fileSize: 200, snippet: '', relationships: {}, }, ] const defaultSelection: SidebarSelection = { kind: 'filter', filter: 'all' } describe('Sidebar', () => { it('renders top nav items (All Notes and Favorites)', () => { render( {}} />) expect(screen.getByText('All Notes')).toBeInTheDocument() expect(screen.getByText('Favorites')).toBeInTheDocument() }) it('renders section group headers with new labels', () => { render( {}} />) expect(screen.getByText('Projects')).toBeInTheDocument() expect(screen.getByText('Experiments')).toBeInTheDocument() expect(screen.getByText('Responsibilities')).toBeInTheDocument() expect(screen.getByText('Procedures')).toBeInTheDocument() expect(screen.getByText('People')).toBeInTheDocument() expect(screen.getByText('Events')).toBeInTheDocument() expect(screen.getByText('Topics')).toBeInTheDocument() }) it('shows entity names under their section groups', () => { render( {}} />) expect(screen.getByText('Build Laputa App')).toBeInTheDocument() expect(screen.getByText('Grow Newsletter')).toBeInTheDocument() expect(screen.getByText('Stock Screener')).toBeInTheDocument() expect(screen.getByText('Write Weekly Essays')).toBeInTheDocument() }) it('shows People and Events as section groups', () => { render( {}} />) expect(screen.getByText('Alice')).toBeInTheDocument() expect(screen.getByText('Kickoff Meeting')).toBeInTheDocument() }) it('collapses and expands sections', () => { render( {}} />) expect(screen.getByText('Build Laputa App')).toBeInTheDocument() fireEvent.click(screen.getByLabelText('Collapse Projects')) expect(screen.queryByText('Build Laputa App')).not.toBeInTheDocument() fireEvent.click(screen.getByLabelText('Expand Projects')) expect(screen.getByText('Build Laputa App')).toBeInTheDocument() }) it('calls onSelect when clicking an entity', () => { const onSelect = vi.fn() render() fireEvent.click(screen.getByText('Build Laputa App')) expect(onSelect).toHaveBeenCalledWith({ kind: 'entity', entry: mockEntries[0], }) }) it('calls onSelect when clicking a section header', () => { const onSelect = vi.fn() render() fireEvent.click(screen.getByText('Projects')) expect(onSelect).toHaveBeenCalledWith({ kind: 'sectionGroup', type: 'Project', }) }) it('calls onSelect with sectionGroup for People', () => { const onSelect = vi.fn() render() fireEvent.click(screen.getByText('People')) expect(onSelect).toHaveBeenCalledWith({ kind: 'sectionGroup', type: 'Person', }) }) it('renders Topics section with topic entries', () => { render( {}} />) expect(screen.getByText('Topics')).toBeInTheDocument() expect(screen.getByText('Software Development')).toBeInTheDocument() expect(screen.getByText('Trading')).toBeInTheDocument() }) it('calls onSelect with topic kind when clicking a topic', () => { const onSelect = vi.fn() render() fireEvent.click(screen.getByText('Software Development')) expect(onSelect).toHaveBeenCalledWith({ kind: 'topic', entry: mockEntries[4], }) }) it('renders + buttons for each section group when onCreateType is provided', () => { const onCreateType = vi.fn() render( {}} onCreateType={onCreateType} />) const createButtons = screen.getAllByTitle(/^New /) expect(createButtons.length).toBe(7) // Projects, Experiments, Responsibilities, Procedures, People, Events, Topics }) it('calls onCreateType with correct type when + button is clicked', () => { const onCreateType = vi.fn() render( {}} onCreateType={onCreateType} />) fireEvent.click(screen.getByTitle('New Project')) expect(onCreateType).toHaveBeenCalledWith('Project') }) it('does not render + buttons when onCreateType is not provided', () => { render( {}} />) expect(screen.queryByTitle('New Project')).not.toBeInTheDocument() }) it('renders commit button even when no modified files', () => { render( {}} onCommitPush={() => {}} />) expect(screen.getByText('Commit & Push')).toBeInTheDocument() }) it('shows badge on commit button when modified files exist', () => { render( {}} modifiedCount={3} onCommitPush={() => {}} />) expect(screen.getByText('3')).toBeInTheDocument() }) })