/** * @jest-environment jsdom */ import { initVersionControl, setupBranchHandlers, handleMergeConflicts } from '../version-control'; describe('Version Control UI', () => { let container; beforeEach(() => { container = document.createElement('div'); container.id = 'version-control-panel'; document.body.appendChild(container); // Mock HTMX window.htmx = { trigger: jest.fn(), ajax: jest.fn(), on: jest.fn() }; }); afterEach(() => { document.body.innerHTML = ''; jest.clearAllMocks(); }); describe('initialization', () => { it('should initialize version control UI', () => { const panel = document.createElement('div'); panel.className = 'version-control-panel'; container.appendChild(panel); initVersionControl(); expect(window.htmx.on).toHaveBeenCalled(); expect(container.querySelector('.version-control-panel')).toBeTruthy(); }); it('should setup branch switch handlers', () => { const switchButton = document.createElement('button'); switchButton.setAttribute('data-branch-id', '1'); switchButton.className = 'branch-switch'; container.appendChild(switchButton); setupBranchHandlers(); switchButton.click(); expect(window.htmx.ajax).toHaveBeenCalledWith( 'POST', '/version-control/switch-branch/', expect.any(Object) ); }); }); describe('branch operations', () => { it('should handle branch creation', () => { const form = document.createElement('form'); form.id = 'create-branch-form'; container.appendChild(form); const event = new Event('submit'); form.dispatchEvent(event); expect(window.htmx.trigger).toHaveBeenCalledWith( form, 'branch-created', expect.any(Object) ); }); it('should update UI after branch switch', () => { const response = { branch_name: 'feature/test', status: 'success' }; const event = new CustomEvent('branchSwitched', { detail: response }); document.dispatchEvent(event); expect(container.querySelector('.current-branch')?.textContent) .toContain('feature/test'); }); }); describe('merge operations', () => { it('should handle merge conflicts', () => { const conflicts = [ { field: 'name', source_value: 'Feature Name', target_value: 'Main Name' } ]; handleMergeConflicts(conflicts); const conflictDialog = document.querySelector('.merge-conflict-dialog'); expect(conflictDialog).toBeTruthy(); expect(conflictDialog.innerHTML).toContain('name'); expect(conflictDialog.innerHTML).toContain('Feature Name'); expect(conflictDialog.innerHTML).toContain('Main Name'); }); it('should submit merge resolution', () => { const resolutionForm = document.createElement('form'); resolutionForm.id = 'merge-resolution-form'; container.appendChild(resolutionForm); const event = new Event('submit'); resolutionForm.dispatchEvent(event); expect(window.htmx.ajax).toHaveBeenCalledWith( 'POST', '/version-control/resolve-conflicts/', expect.any(Object) ); }); }); describe('error handling', () => { it('should display error messages', () => { const errorEvent = new CustomEvent('showError', { detail: { message: 'Test error message' } }); document.dispatchEvent(errorEvent); const errorMessage = document.querySelector('.error-message'); expect(errorMessage).toBeTruthy(); expect(errorMessage.textContent).toContain('Test error message'); }); it('should clear error messages', () => { const errorMessage = document.createElement('div'); errorMessage.className = 'error-message'; container.appendChild(errorMessage); const clearEvent = new Event('clearErrors'); document.dispatchEvent(clearEvent); expect(container.querySelector('.error-message')).toBeFalsy(); }); }); describe('loading states', () => { it('should show loading indicator during operations', () => { const loadingEvent = new Event('versionControlLoading'); document.dispatchEvent(loadingEvent); const loader = document.querySelector('.version-control-loader'); expect(loader).toBeTruthy(); expect(loader.style.display).toBe('block'); }); it('should hide loading indicator after operations', () => { const loader = document.createElement('div'); loader.className = 'version-control-loader'; container.appendChild(loader); const doneEvent = new Event('versionControlLoaded'); document.dispatchEvent(doneEvent); expect(loader.style.display).toBe('none'); }); }); describe('UI updates', () => { it('should update branch list after operations', () => { const branchList = document.createElement('ul'); branchList.className = 'branch-list'; container.appendChild(branchList); const updateEvent = new CustomEvent('updateBranchList', { detail: { branches: [ { name: 'main', active: true }, { name: 'feature/test', active: false } ] } }); document.dispatchEvent(updateEvent); const listItems = branchList.querySelectorAll('li'); expect(listItems.length).toBe(2); expect(listItems[0].textContent).toContain('main'); expect(listItems[1].textContent).toContain('feature/test'); }); it('should highlight active branch', () => { const branchItems = [ { name: 'main', active: true }, { name: 'feature/test', active: false } ].map(branch => { const item = document.createElement('li'); item.textContent = branch.name; item.className = 'branch-item'; if (branch.active) item.classList.add('active'); return item; }); const branchList = document.createElement('ul'); branchList.className = 'branch-list'; branchList.append(...branchItems); container.appendChild(branchList); const activeItem = branchList.querySelector('.branch-item.active'); expect(activeItem).toBeTruthy(); expect(activeItem.textContent).toBe('main'); }); }); });