Files
HIPCTF2/tests/frontend/admin-challenges-list.spec.ts
T

101 lines
3.3 KiB
TypeScript

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> = {}): 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('');
});
});