diff --git a/code/websites/pokedex.online/src/components/gamemaster/FileSelector.vue b/code/websites/pokedex.online/src/components/gamemaster/FileSelector.vue new file mode 100644 index 0000000..299d7f0 --- /dev/null +++ b/code/websites/pokedex.online/src/components/gamemaster/FileSelector.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/code/websites/pokedex.online/tests/unit/components/gamemaster/FileSelector.test.js b/code/websites/pokedex.online/tests/unit/components/gamemaster/FileSelector.test.js new file mode 100644 index 0000000..c0f81b2 --- /dev/null +++ b/code/websites/pokedex.online/tests/unit/components/gamemaster/FileSelector.test.js @@ -0,0 +1,131 @@ +/** + * FileSelector Component Tests + * Verifies file selector UI and behavior + */ + +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { mount } from '@vue/test-utils'; +import { ref } from 'vue'; +import FileSelector from '../../../../src/components/gamemaster/FileSelector.vue'; +import { useGamemasterFiles } from '../../../../src/composables/useGamemasterFiles.js'; + +vi.mock('../../../../src/composables/useGamemasterFiles.js', () => ({ + useGamemasterFiles: vi.fn() +})); + +const createFilesMock = overrides => ({ + selectedFile: ref(''), + fileContent: ref(''), + fileLines: ref([]), + uniqueFiles: ref([]), + isLoading: ref(false), + fileError: ref(null), + loadStatus: vi.fn(), + formatSize: vi.fn(size => `${size} B`), + formatFileName: vi.fn(name => name), + getFileType: vi.fn(name => name.replace('.json', '')), + ...overrides +}); + +describe('FileSelector Component', () => { + let mockFiles; + + beforeEach(() => { + mockFiles = createFilesMock(); + useGamemasterFiles.mockReturnValue(mockFiles); + }); + + it('renders file selector with placeholder option', () => { + const wrapper = mount(FileSelector, { + props: { client: {} } + }); + + const select = wrapper.find('select'); + expect(select.exists()).toBe(true); + expect(select.find('option').text()).toContain('Choose a file'); + }); + + it('calls loadStatus on mount', () => { + mount(FileSelector, { + props: { client: {} } + }); + + expect(mockFiles.loadStatus).toHaveBeenCalled(); + }); + + it('renders available file options', () => { + mockFiles.uniqueFiles.value = [ + { filename: 'pokemon.json', size: 100 }, + { filename: 'moves.json', size: 200 } + ]; + + const wrapper = mount(FileSelector, { + props: { client: {} } + }); + + const options = wrapper.findAll('option'); + expect(options.length).toBe(3); // Placeholder + 2 files + expect(options[1].text()).toContain('pokemon.json'); + expect(options[2].text()).toContain('moves.json'); + }); + + it('disables selector when loading', () => { + mockFiles.isLoading.value = true; + + const wrapper = mount(FileSelector, { + props: { client: {} } + }); + + const select = wrapper.find('select'); + expect(select.attributes('disabled')).toBeDefined(); + }); + + it('disables selector when no files', () => { + mockFiles.uniqueFiles.value = []; + + const wrapper = mount(FileSelector, { + props: { client: {} } + }); + + const select = wrapper.find('select'); + expect(select.attributes('disabled')).toBeDefined(); + }); + + it('shows file info when content is loaded', () => { + mockFiles.fileContent.value = '{"test": true}'; + mockFiles.fileLines.value = ['{', ' "test": true', '}']; + mockFiles.selectedFile.value = 'pokemon'; + mockFiles.uniqueFiles.value = [ + { filename: 'pokemon.json', size: 300 } + ]; + + const wrapper = mount(FileSelector, { + props: { client: {} } + }); + + const info = wrapper.find('.file-info'); + expect(info.exists()).toBe(true); + expect(info.text()).toContain('3 lines'); + expect(info.text()).toContain('300 B'); + }); + + it('shows loading status text', () => { + mockFiles.isLoading.value = true; + + const wrapper = mount(FileSelector, { + props: { client: {} } + }); + + expect(wrapper.find('.file-status').text()).toContain('Loading'); + }); + + it('shows error message', () => { + mockFiles.fileError.value = 'Failed to load file'; + + const wrapper = mount(FileSelector, { + props: { client: {} } + }); + + expect(wrapper.find('.file-error').text()).toContain('Failed to load file'); + }); +});