🔍 Add unit tests for GamemasterExplorer view
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
/**
|
||||
* GamemasterExplorer View Tests
|
||||
* Integration tests verifying the refactored component works correctly
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { ref } from 'vue';
|
||||
import GamemasterExplorer from '../../../src/views/GamemasterExplorer.vue';
|
||||
|
||||
// Mock composables
|
||||
vi.mock('../../../src/composables/useGamemasterFiles.js', () => ({
|
||||
useGamemasterFiles: vi.fn(() => ({
|
||||
selectedFile: ref('pokemon.json'),
|
||||
fileContent: ref('{"test": "data"}'),
|
||||
fileLines: ref(['{', ' "test": "data"', '}']),
|
||||
displayLines: ref(['{', ' "test": "data"', '}']),
|
||||
isLoading: ref(false),
|
||||
fileError: ref(null),
|
||||
preferences: ref({
|
||||
lineWrap: false,
|
||||
darkMode: false,
|
||||
showLineNumbers: true,
|
||||
performanceMode: 'auto'
|
||||
}),
|
||||
hasFiles: ref(true),
|
||||
fileTooLarge: ref(false),
|
||||
loadStatus: vi.fn(),
|
||||
formatSize: vi.fn(size => `${size} B`),
|
||||
formatFileName: vi.fn(name => name),
|
||||
getFileType: vi.fn(name => name.replace('.json', ''))
|
||||
}))
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/composables/useGamemasterSearch.js', () => ({
|
||||
useGamemasterSearch: vi.fn(() => ({
|
||||
searchQuery: ref(''),
|
||||
searchResults: ref([]),
|
||||
currentResultIndex: ref(-1),
|
||||
isSearching: ref(false),
|
||||
searchError: ref(null),
|
||||
executeSearch: vi.fn(),
|
||||
clearSearch: vi.fn(),
|
||||
goToNextResult: vi.fn(),
|
||||
goToPrevResult: vi.fn()
|
||||
}))
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/composables/useLineSelection.js', () => ({
|
||||
useLineSelection: vi.fn(() => ({
|
||||
selectedLines: ref(new Set()),
|
||||
hasSelection: ref(false),
|
||||
selectionCount: ref(0),
|
||||
toggleLineSelection: vi.fn(),
|
||||
clearSelection: vi.fn(),
|
||||
selectAll: vi.fn(),
|
||||
copySelected: vi.fn(),
|
||||
exportSelected: vi.fn()
|
||||
}))
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/composables/useJsonFilter.js', () => ({
|
||||
default: vi.fn(() => ({
|
||||
filterProperty: ref(''),
|
||||
filterValue: ref(''),
|
||||
filterMode: ref('equals'),
|
||||
filteredData: ref([]),
|
||||
setFilter: vi.fn(),
|
||||
clearFilters: vi.fn(),
|
||||
getUniqueValues: vi.fn(() => [])
|
||||
}))
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/composables/useKeyboardShortcuts.js', () => ({
|
||||
useKeyboardShortcuts: vi.fn()
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/composables/useUrlState.js', () => ({
|
||||
useUrlState: vi.fn()
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/composables/useClipboard.js', () => ({
|
||||
useClipboard: vi.fn(() => ({
|
||||
copied: ref(false),
|
||||
error: ref(null),
|
||||
copy: vi.fn()
|
||||
}))
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/utilities/gamemaster-client.js', () => ({
|
||||
GamemasterClient: vi.fn(() => ({
|
||||
getStatus: vi.fn(),
|
||||
getFile: vi.fn()
|
||||
}))
|
||||
}));
|
||||
|
||||
describe('GamemasterExplorer', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders successfully', () => {
|
||||
const wrapper = mount(GamemasterExplorer);
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('shows loading state when isLoading is true', () => {
|
||||
const { useGamemasterFiles } = await import(
|
||||
'../../../src/composables/useGamemasterFiles.js'
|
||||
);
|
||||
useGamemasterFiles.mockReturnValueOnce({
|
||||
selectedFile: ref(''),
|
||||
fileContent: ref(''),
|
||||
fileLines: ref([]),
|
||||
displayLines: ref([]),
|
||||
isLoading: ref(true),
|
||||
fileError: ref(null),
|
||||
preferences: ref({}),
|
||||
hasFiles: ref(false),
|
||||
fileTooLarge: ref(false),
|
||||
loadStatus: vi.fn()
|
||||
});
|
||||
|
||||
const wrapper = mount(GamemasterExplorer);
|
||||
expect(wrapper.find('.loading-state').exists()).toBe(true);
|
||||
expect(wrapper.text()).toContain('Loading Gamemaster Explorer');
|
||||
});
|
||||
|
||||
it('shows error state when fileError exists', () => {
|
||||
const { useGamemasterFiles } = await import(
|
||||
'../../../src/composables/useGamemasterFiles.js'
|
||||
);
|
||||
useGamemasterFiles.mockReturnValueOnce({
|
||||
selectedFile: ref(''),
|
||||
fileContent: ref(''),
|
||||
fileLines: ref([]),
|
||||
displayLines: ref([]),
|
||||
isLoading: ref(false),
|
||||
fileError: ref('Failed to load files'),
|
||||
preferences: ref({}),
|
||||
hasFiles: ref(false),
|
||||
fileTooLarge: ref(false),
|
||||
loadStatus: vi.fn()
|
||||
});
|
||||
|
||||
const wrapper = mount(GamemasterExplorer);
|
||||
expect(wrapper.find('.error-state').exists()).toBe(true);
|
||||
expect(wrapper.text()).toContain('Failed to load files');
|
||||
});
|
||||
|
||||
it('shows no files state when hasFiles is false', () => {
|
||||
const { useGamemasterFiles } = await import(
|
||||
'../../../src/composables/useGamemasterFiles.js'
|
||||
);
|
||||
useGamemasterFiles.mockReturnValueOnce({
|
||||
selectedFile: ref(''),
|
||||
fileContent: ref(''),
|
||||
fileLines: ref([]),
|
||||
displayLines: ref([]),
|
||||
isLoading: ref(false),
|
||||
fileError: ref(null),
|
||||
preferences: ref({}),
|
||||
hasFiles: ref(false),
|
||||
fileTooLarge: ref(false),
|
||||
loadStatus: vi.fn()
|
||||
});
|
||||
|
||||
const wrapper = mount(GamemasterExplorer);
|
||||
expect(wrapper.find('.no-files-state').exists()).toBe(true);
|
||||
expect(wrapper.text()).toContain('No Gamemaster Files Available');
|
||||
});
|
||||
|
||||
it('renders main explorer interface when files are loaded', () => {
|
||||
const wrapper = mount(GamemasterExplorer);
|
||||
expect(wrapper.find('.explorer-container').exists()).toBe(true);
|
||||
expect(wrapper.find('.explorer-header').exists()).toBe(true);
|
||||
expect(wrapper.text()).toContain('Gamemaster Explorer');
|
||||
});
|
||||
|
||||
it('includes all child components', () => {
|
||||
const wrapper = mount(GamemasterExplorer);
|
||||
|
||||
// Check for FileSelector component
|
||||
expect(wrapper.findComponent({ name: 'FileSelector' }).exists()).toBe(true);
|
||||
|
||||
// Check for SearchBar component
|
||||
expect(wrapper.findComponent({ name: 'SearchBar' }).exists()).toBe(true);
|
||||
|
||||
// Check for FilterPanel component
|
||||
expect(wrapper.findComponent({ name: 'FilterPanel' }).exists()).toBe(true);
|
||||
|
||||
// Check for JsonViewer component
|
||||
expect(wrapper.findComponent({ name: 'JsonViewer' }).exists()).toBe(true);
|
||||
|
||||
// Check for ActionToolbar component
|
||||
expect(wrapper.findComponent({ name: 'ActionToolbar' }).exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('toggles help panel', async () => {
|
||||
const wrapper = mount(GamemasterExplorer);
|
||||
|
||||
expect(wrapper.find('.help-panel').exists()).toBe(false);
|
||||
|
||||
// Click help button
|
||||
const helpButton = wrapper.findAll('.btn-icon')[0];
|
||||
await helpButton.trigger('click');
|
||||
|
||||
expect(wrapper.find('.help-panel').exists()).toBe(true);
|
||||
expect(wrapper.text()).toContain('Keyboard Shortcuts');
|
||||
});
|
||||
|
||||
it('toggles settings panel', async () => {
|
||||
const wrapper = mount(GamemasterExplorer);
|
||||
|
||||
expect(wrapper.find('.settings-panel').exists()).toBe(false);
|
||||
|
||||
// Click settings button
|
||||
const settingsButton = wrapper.findAll('.btn-icon')[1];
|
||||
await settingsButton.trigger('click');
|
||||
|
||||
expect(wrapper.find('.settings-panel').exists()).toBe(true);
|
||||
expect(wrapper.text()).toContain('Settings');
|
||||
});
|
||||
|
||||
it('has back to home link', () => {
|
||||
const wrapper = mount(GamemasterExplorer);
|
||||
const backLink = wrapper.find('.back-button');
|
||||
|
||||
expect(backLink.exists()).toBe(true);
|
||||
expect(backLink.attributes('to')).toBe('/');
|
||||
});
|
||||
|
||||
it('calls loadStatus on mount', () => {
|
||||
const { useGamemasterFiles } = await import(
|
||||
'../../../src/composables/useGamemasterFiles.js'
|
||||
);
|
||||
const mockLoadStatus = vi.fn();
|
||||
useGamemasterFiles.mockReturnValueOnce({
|
||||
selectedFile: ref(''),
|
||||
fileContent: ref(''),
|
||||
fileLines: ref([]),
|
||||
displayLines: ref([]),
|
||||
isLoading: ref(false),
|
||||
fileError: ref(null),
|
||||
preferences: ref({}),
|
||||
hasFiles: ref(true),
|
||||
fileTooLarge: ref(false),
|
||||
loadStatus: mockLoadStatus
|
||||
});
|
||||
|
||||
mount(GamemasterExplorer);
|
||||
|
||||
// loadStatus should be called on mount
|
||||
expect(mockLoadStatus).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('computes filterData from fileContent', () => {
|
||||
const wrapper = mount(GamemasterExplorer);
|
||||
|
||||
// Component should parse JSON content for filtering
|
||||
const filterData = wrapper.vm.filterData;
|
||||
expect(filterData).toBeDefined();
|
||||
});
|
||||
|
||||
it('displays toast messages for clipboard operations', async () => {
|
||||
const { useClipboard } = await import(
|
||||
'../../../src/composables/useClipboard.js'
|
||||
);
|
||||
useClipboard.mockReturnValueOnce({
|
||||
copied: ref(true),
|
||||
error: ref(null),
|
||||
copy: vi.fn()
|
||||
});
|
||||
|
||||
const wrapper = mount(GamemasterExplorer);
|
||||
expect(wrapper.find('.toast.success').exists()).toBe(true);
|
||||
expect(wrapper.text()).toContain('Copied to clipboard');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user