Files
memory-infrastructure-palace/code/websites/pokedex.online/tests/unit/composables/useGamemasterFiles.test.js

421 lines
13 KiB
JavaScript

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { ref } from 'vue';
import { useGamemasterFiles } from '@/composables/useGamemasterFiles.js';
describe('useGamemasterFiles', () => {
let mockClient;
let composable;
beforeEach(() => {
// Mock GamemasterClient
mockClient = {
getStatus: vi.fn(() =>
Promise.resolve({
available: [
{ filename: 'pokemon.json', size: 5000 },
{ filename: 'moves.json', size: 3000 },
{ filename: 'allForms.json', size: 8000 },
{ filename: 'raw.json', size: 20000 }
]
})
),
getPokemon: vi.fn(() =>
Promise.resolve({
pokemon: [{ name: 'pikachu', id: 25 }]
})
),
getMoves: vi.fn(() =>
Promise.resolve({
moves: [{ name: 'thunderbolt', id: 24 }]
})
),
getAllForms: vi.fn(() =>
Promise.resolve({
forms: [{ name: 'pikachu-gmax' }]
})
),
getRaw: vi.fn(() =>
Promise.resolve({
raw: [{ data: 'raw content' }]
})
)
};
composable = useGamemasterFiles(mockClient);
});
afterEach(() => {
vi.clearAllMocks();
});
describe('initialization', () => {
it('should initialize with empty state', () => {
expect(composable.selectedFile.value).toBe('');
expect(composable.fileContent.value).toBe('');
expect(composable.fileLines.value).toEqual([]);
expect(composable.displayLines.value).toEqual([]);
expect(composable.isLoading.value).toBe(false);
expect(composable.fileError.value).toBeNull();
});
it('should have default preferences', () => {
expect(composable.preferences.value).toBeDefined();
expect(composable.preferences.value.darkMode).toBe(false);
expect(composable.preferences.value.showLineNumbers).toBe(true);
});
});
describe('loadStatus', () => {
it('should fetch file list from server', async () => {
await composable.loadStatus();
expect(mockClient.getStatus).toHaveBeenCalled();
expect(composable.status.value.available).toHaveLength(4);
});
it('should set error on fetch failure', async () => {
mockClient.getStatus.mockRejectedValueOnce(new Error('Network error'));
await composable.loadStatus();
expect(composable.fileError.value).toBe('Network error');
});
it('should auto-load last file if preference set', async () => {
composable.preferences.value.lastFile = 'pokemon';
mockClient.getPokemon.mockResolvedValueOnce({ pokemon: [] });
await composable.loadStatus();
expect(composable.selectedFile.value).toBe('pokemon');
expect(mockClient.getPokemon).toHaveBeenCalled();
});
it('should set loading state during fetch', async () => {
const loadingStates = [];
const originalSetTimeout = setTimeout;
await composable.loadStatus();
expect(composable.isLoading.value).toBe(false);
});
});
describe('loadFile', () => {
beforeEach(async () => {
await composable.loadStatus();
});
it('should return early if no file selected', async () => {
composable.selectedFile.value = '';
await composable.loadFile();
expect(mockClient.getPokemon).not.toHaveBeenCalled();
});
it('should load pokemon file', async () => {
composable.selectedFile.value = 'pokemon';
await composable.loadFile();
expect(mockClient.getPokemon).toHaveBeenCalled();
expect(composable.fileContent.value).toContain('pikachu');
expect(composable.fileLines.value.length).toBeGreaterThan(0);
});
it('should load moves file', async () => {
composable.selectedFile.value = 'moves';
await composable.loadFile();
expect(mockClient.getMoves).toHaveBeenCalled();
expect(composable.fileContent.value).toContain('thunderbolt');
});
it('should load allForms file', async () => {
composable.selectedFile.value = 'allForms';
await composable.loadFile();
expect(mockClient.getAllForms).toHaveBeenCalled();
expect(composable.fileContent.value).toContain('pikachu-gmax');
});
it('should load raw file', async () => {
composable.selectedFile.value = 'raw';
await composable.loadFile();
expect(mockClient.getRaw).toHaveBeenCalled();
expect(composable.fileContent.value).toContain('raw content');
});
it('should reject raw file if too large', async () => {
composable.status.value.available = [
{
filename: 'raw.json',
size: 100 * 1024 * 1024 // 100MB
}
];
composable.selectedFile.value = 'raw';
await composable.loadFile();
expect(composable.fileError.value).toContain('very large');
expect(composable.fileContent.value).toBe('');
});
it('should set display lines for small files', async () => {
composable.selectedFile.value = 'pokemon';
await composable.loadFile();
expect(composable.displayLines.value.length).toBeGreaterThan(0);
expect(composable.displayLines.value[0]).toHaveProperty('lineNumber');
expect(composable.displayLines.value[0]).toHaveProperty('content');
expect(composable.displayLines.value[0]).toHaveProperty('hasMatch');
});
it('should limit displayed lines to 10000', async () => {
composable.selectedFile.value = 'pokemon';
await composable.loadFile();
expect(composable.displayLines.value.length).toBeLessThanOrEqual(
composable.LINES_TO_DISPLAY
);
});
it('should save last file preference', async () => {
await composable.loadStatus(); // Load status first
composable.selectedFile.value = 'moves';
await composable.loadFile();
expect(composable.preferences.value.lastFile).toBe('moves');
});
it('should handle unknown file type', async () => {
composable.selectedFile.value = 'unknown';
await composable.loadFile();
expect(composable.fileError.value).toContain('Unknown file type');
});
it('should handle file loading correctly', async () => {
await composable.loadStatus();
composable.selectedFile.value = 'pokemon';
await composable.loadFile();
expect(composable.fileContent.value).toBeDefined();
expect(composable.fileLines.value.length).toBeGreaterThan(0);
});
});
describe('clearFileSelection', () => {
beforeEach(async () => {
composable.selectedFile.value = 'pokemon';
composable.fileContent.value = 'test content';
composable.fileLines.value = ['line1', 'line2'];
composable.jsonPaths.value = ['/path1'];
composable.fileError.value = 'Some error';
});
it('should clear all file state', () => {
composable.clearFileSelection();
expect(composable.selectedFile.value).toBe('');
expect(composable.fileContent.value).toBe('');
expect(composable.fileLines.value).toEqual([]);
expect(composable.displayLines.value).toEqual([]);
expect(composable.jsonPaths.value).toEqual([]);
expect(composable.fileError.value).toBeNull();
});
});
describe('formatSize', () => {
it('should format bytes correctly', () => {
expect(composable.formatSize(0)).toBe('0 B');
expect(composable.formatSize(1024)).toContain('KB');
expect(composable.formatSize(1024 * 1024)).toContain('MB');
});
it('should handle null/undefined', () => {
expect(composable.formatSize(null)).toBe('0 B');
expect(composable.formatSize(undefined)).toBe('0 B');
});
});
describe('getFileType', () => {
it('should identify pokemon file', () => {
expect(composable.getFileType('pokemon.json')).toBe('pokemon');
});
it('should identify moves file', () => {
expect(composable.getFileType('moves.json')).toBe('moves');
});
it('should identify allForms file', () => {
expect(composable.getFileType('allForms.json')).toBe('allForms');
expect(composable.getFileType('AllForms.json')).toBe('allForms');
});
it('should identify raw file', () => {
expect(composable.getFileType('raw.json')).toBe('raw');
});
it('should return empty string for unknown type', () => {
expect(composable.getFileType('unknown.json')).toBe('');
});
});
describe('formatFileName', () => {
it('should format pokemon file name', () => {
expect(composable.formatFileName('pokemon.json')).toBe('Pokemon');
});
it('should format moves file name', () => {
expect(composable.formatFileName('moves.json')).toBe('Moves');
});
it('should format allForms file name', () => {
expect(composable.formatFileName('allForms.json')).toBe(
'Pokemon All Forms'
);
});
it('should format raw file name', () => {
expect(composable.formatFileName('raw.json')).toBe('Raw Gamemaster');
});
});
describe('computed properties', () => {
describe('availableFiles', () => {
it('should return files from status', async () => {
await composable.loadStatus();
expect(composable.availableFiles.value).toHaveLength(4);
});
it('should return empty array initially', () => {
expect(composable.availableFiles.value).toEqual([]);
});
});
describe('uniqueFiles', () => {
it('should return unique files sorted by type', async () => {
await composable.loadStatus();
expect(composable.uniqueFiles.value.length).toBeGreaterThan(0);
});
it('should sort files by type order', async () => {
await composable.loadStatus();
const types = composable.uniqueFiles.value.map(f =>
composable.getFileType(f.filename)
);
expect(types.indexOf('pokemon')).toBeLessThan(
types.indexOf('allForms')
);
});
});
describe('hasFiles', () => {
it('should be false initially', () => {
expect(composable.hasFiles.value).toBe(false);
});
it('should be true after loading status', async () => {
await composable.loadStatus();
expect(composable.hasFiles.value).toBe(true);
});
});
describe('fileTooLarge', () => {
it('should be false for small files', async () => {
composable.selectedFile.value = 'pokemon';
await composable.loadFile();
expect(composable.fileTooLarge.value).toBe(false);
});
it('should compute correctly based on line count', () => {
// Manually set up a large line array
composable.fileLines.value = Array(15000).fill('line');
expect(composable.fileTooLarge.value).toBe(true);
});
});
});
describe('updateDisplayLines', () => {
beforeEach(async () => {
composable.selectedFile.value = 'pokemon';
await composable.loadFile();
});
it('should update display lines range', () => {
const originalCount = composable.displayLines.value.length;
composable.updateDisplayLines(0, 100);
expect(composable.displayLines.value.length).toBeLessThanOrEqual(100);
});
it('should set correct line numbers', () => {
// Create enough lines for this test
composable.fileLines.value = Array(200).fill('test line');
composable.updateDisplayLines(50, 150);
expect(composable.displayLines.value.length).toBeGreaterThan(0);
if (composable.displayLines.value.length > 0) {
expect(composable.displayLines.value[0].lineNumber).toBe(51);
}
});
});
describe('expandDisplayLinesToInclude', () => {
beforeEach(async () => {
// Create large file with many items that will create many lines
const largeData = {
items: Array(15000).fill({ id: 1, data: 'test' })
};
mockClient.getPokemon.mockResolvedValueOnce(largeData);
composable.selectedFile.value = 'pokemon';
await composable.loadFile();
});
it('should not expand if line is visible', () => {
const originalLength = composable.displayLines.value.length;
composable.expandDisplayLinesToInclude(100);
expect(composable.displayLines.value.length).toBe(originalLength);
});
it('should expand to include line number', () => {
if (composable.fileLines.value.length > 15000) {
composable.expandDisplayLinesToInclude(15000);
expect(composable.displayLines.value.length).toBeGreaterThan(
composable.LINES_TO_DISPLAY
);
}
});
it('should not exceed total file lines', () => {
composable.expandDisplayLinesToInclude(999999);
expect(composable.displayLines.value.length).toBeLessThanOrEqual(
composable.fileLines.value.length
);
});
});
describe('file selection watch', () => {
it('should load file when selectedFile changes', async () => {
composable.selectedFile.value = 'pokemon';
await new Promise(r => setTimeout(r, 50));
expect(mockClient.getPokemon).toHaveBeenCalled();
});
it('should not load when selectedFile is empty', async () => {
composable.selectedFile.value = '';
await new Promise(r => setTimeout(r, 50));
expect(mockClient.getPokemon).not.toHaveBeenCalled();
});
});
describe('constants', () => {
it('should have correct constants', () => {
expect(composable.LINES_TO_DISPLAY).toBe(10000);
expect(composable.MAX_RAW_FILE_SIZE).toBe(50 * 1024 * 1024);
});
});
});