+
+
+
+
+
+ {{ fileLines.length.toLocaleString() }} lines
+
+ • {{ formatSize(selectedFileMeta.size) }}
+
+
+
+ Loading...
+ {{ fileError }}
+
+
+
+
+
+
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');
+ });
+});