/** * JsonViewer Component Tests * Verifies JSON display and line selection UI */ import { describe, it, expect, beforeEach, vi } from 'vitest'; import { mount } from '@vue/test-utils'; import { ref } from 'vue'; import JsonViewer from '../../../../src/components/gamemaster/JsonViewer.vue'; import { useLineSelection } from '../../../../src/composables/useLineSelection.js'; vi.mock('../../../../src/composables/useLineSelection.js', () => ({ useLineSelection: vi.fn() })); const createSelectionMock = overrides => ({ selectedLines: ref(new Set()), toggleLineSelection: vi.fn(), ...overrides }); describe('JsonViewer Component', () => { let selectionMock; beforeEach(() => { selectionMock = createSelectionMock(); useLineSelection.mockReturnValue(selectionMock); }); const baseProps = { displayLines: [ { lineNumber: 1, content: '{', hasMatch: false }, { lineNumber: 2, content: ' "name": "Pikachu"', hasMatch: true } ], fileContent: '{\n "name": "Pikachu"\n}', selectedFile: 'pokemon', preferences: { showLineNumbers: true, darkMode: false, lineWrap: false }, searchResults: [1], currentResultIndex: 0, highlightConfig: { theme: 'github', language: 'json' }, lineHeight: 20 }; it('does not render when fileContent is empty', () => { const wrapper = mount(JsonViewer, { props: { ...baseProps, fileContent: '' } }); expect(wrapper.find('.content-viewer').exists()).toBe(false); }); it('renders lines with line numbers', () => { selectionMock.selectedLines.value.add(2); const wrapper = mount(JsonViewer, { props: baseProps }); const lines = wrapper.findAll('.line'); expect(lines.length).toBe(2); expect(lines[1].classes()).toContain('selected'); expect(wrapper.findAll('.line-number').length).toBe(2); }); it('calls toggleLineSelection on line click', async () => { const wrapper = mount(JsonViewer, { props: baseProps }); const lines = wrapper.findAll('.line'); await lines[0].trigger('click'); expect(selectionMock.toggleLineSelection).toHaveBeenCalled(); }); it('marks the current search result line', () => { const wrapper = mount(JsonViewer, { props: baseProps }); const current = wrapper.findAll('.line')[1]; expect(current.classes()).toContain('current-result'); }); it('shows warning banner when file is too large', () => { const wrapper = mount(JsonViewer, { props: { ...baseProps, fileTooLarge: true } }); expect(wrapper.find('.warning-banner').exists()).toBe(true); }); it('uses RecycleScroller for large line counts', () => { const largeLines = Array.from({ length: 1001 }, (_, index) => ({ lineNumber: index + 1, content: `line ${index + 1}`, hasMatch: false })); const wrapper = mount(JsonViewer, { props: { ...baseProps, displayLines: largeLines }, global: { stubs: { RecycleScroller: { template: '
', props: ['items'] } } } }); expect(wrapper.find('.scroller').exists()).toBe(true); }); });