import { AdminChallengeListItem, ImportPreviewResponse, ImportResultResponse, } from '../../frontend/src/app/core/services/admin.service'; import { buildExportFilename, categoryIconSrc, deriveEmptyState, difficultyClass, formatFileSize, summarizeImportPreview, summarizeImportResult, } from '../../frontend/src/app/features/admin/challenges/challenges.pure'; function makeRow(overrides: Partial = {}): AdminChallengeListItem { return { id: 'c1', name: 'Alpha', categoryAbbreviation: 'CRY', categoryIconPath: '/uploads/icons/CRY.png', difficulty: 'LOW', protocol: 'WEB', enabled: true, createdAt: '2026-07-22T00:00:00.000Z', updatedAt: '2026-07-22T00:00:00.000Z', fileCount: 0, solveCount: 0, ...overrides, }; } describe('challenges.pure helpers', () => { it('formatFileSize formats bytes / KB / MB', () => { expect(formatFileSize(0)).toBe('0 B'); expect(formatFileSize(512)).toBe('512 B'); expect(formatFileSize(2048)).toMatch(/KB$/); expect(formatFileSize(5 * 1024 * 1024)).toMatch(/MB$/); }); it('buildExportFilename derives an ISO-date filename', () => { const fixed = new Date('2026-07-22T12:00:00.000Z'); expect(buildExportFilename(fixed)).toBe('challenges-export-2026-07-22.json'); }); it('deriveEmptyState distinguishes empty-list, no-results-search, and loaded states', () => { expect(deriveEmptyState([], '', false)).toEqual({ kind: 'empty-list' }); expect(deriveEmptyState([], 'crypto', false)).toEqual({ kind: 'no-results-search', search: 'crypto', }); expect(deriveEmptyState([makeRow()], '', false)).toBeNull(); expect(deriveEmptyState([], '', true)).toBeNull(); }); it('summarizeImportPreview builds a single-line preview summary', () => { const preview: ImportPreviewResponse = { total: 3, conflicts: ['Alpha', 'Beta'], unknownCategories: ['UNKNOWN'], warnings: [], }; const out = summarizeImportPreview(preview); expect(out).toContain('Import 3 challenges'); expect(out).toContain('overwritten'); expect(out).toContain('missing categories'); }); it('summarizeImportPreview handles empty document', () => { const preview: ImportPreviewResponse = { total: 0, conflicts: [], unknownCategories: [], warnings: [], }; expect(summarizeImportPreview(preview)).toContain('no challenges'); }); it('summarizeImportResult reports imported/skipped/warning counts', () => { const r: ImportResultResponse = { imported: 2, skipped: [{ name: 'X', reason: 'Unknown category: Y' }], warnings: ['1 skipped.'], }; const out = summarizeImportResult(r); expect(out).toContain('Imported 2'); expect(out).toContain('Skipped 1'); expect(out).toContain('1 warning'); }); it('difficultyClass returns the appropriate badge class', () => { expect(difficultyClass('LOW')).toContain('difficulty-low'); expect(difficultyClass('MEDIUM')).toContain('difficulty-medium'); expect(difficultyClass('HIGH')).toContain('difficulty-high'); }); it('categoryIconSrc returns the supplied icon path or an empty string', () => { expect(categoryIconSrc('/uploads/icons/X.png', 'X')).toBe('/uploads/icons/X.png'); expect(categoryIconSrc('', 'X')).toBe(''); }); });