99 lines
3.4 KiB
TypeScript
99 lines
3.4 KiB
TypeScript
import {
|
|
importErrorMessage,
|
|
summarizeImportResult,
|
|
} from '../../frontend/src/app/features/admin/challenges/challenges.pure';
|
|
|
|
describe('Job 903: import error + result helpers', () => {
|
|
it('summarizeImportResult reports imported/skipped/warning counts', () => {
|
|
const r = {
|
|
imported: 2,
|
|
skipped: [{ name: 'X', reason: 'Unknown category: Y' }],
|
|
warnings: ['1 skipped.'],
|
|
};
|
|
const out = summarizeImportResult(r as any);
|
|
expect(out).toContain('Imported 2');
|
|
expect(out).toContain('Skipped 1');
|
|
expect(out).toContain('1 warning');
|
|
});
|
|
|
|
it('importErrorMessage prefers the nested error.message envelope', () => {
|
|
expect(importErrorMessage({ error: { message: 'Server said no' } }, 'fallback')).toBe('Server said no');
|
|
});
|
|
|
|
it('importErrorMessage falls back to err.message', () => {
|
|
expect(importErrorMessage({ message: 'boom' }, 'fallback')).toBe('boom');
|
|
});
|
|
|
|
it('importErrorMessage falls back to the supplied default when no message is available', () => {
|
|
expect(importErrorMessage({}, 'fallback')).toBe('fallback');
|
|
expect(importErrorMessage(null, 'fallback')).toBe('fallback');
|
|
});
|
|
});
|
|
|
|
describe('Job 903: confirmed import handler contract', () => {
|
|
function makeDoc() {
|
|
return { format: 'hipctf-challenges', version: 1, exportedAt: '', challenges: [] };
|
|
}
|
|
|
|
it('a successful confirm yields a closed-modal state and a status banner', async () => {
|
|
const state = {
|
|
importOpen: true as boolean,
|
|
importDoc: makeDoc() as any,
|
|
importPreview: { total: 2, conflicts: [], unknownCategories: [], warnings: [] } as any,
|
|
importResult: null as any,
|
|
importError: null as string | null,
|
|
importing: false,
|
|
statusMessage: null as string | null,
|
|
};
|
|
|
|
// Simulate onImportConfirm body using pure helpers.
|
|
try {
|
|
const result = { imported: 2, skipped: [], warnings: [] };
|
|
state.statusMessage = summarizeImportResult(result as any);
|
|
await Promise.resolve();
|
|
// Close-modal branch (the new behavior).
|
|
state.importOpen = false;
|
|
state.importDoc = null;
|
|
state.importPreview = null;
|
|
state.importResult = null;
|
|
state.importError = null;
|
|
} catch (err) {
|
|
state.importError = importErrorMessage(err, 'Failed to import challenges');
|
|
} finally {
|
|
state.importing = false;
|
|
}
|
|
|
|
expect(state.importOpen).toBe(false);
|
|
expect(state.importDoc).toBeNull();
|
|
expect(state.importPreview).toBeNull();
|
|
expect(state.importResult).toBeNull();
|
|
expect(state.importError).toBeNull();
|
|
expect(state.statusMessage).toContain('Imported 2');
|
|
expect(state.importing).toBe(false);
|
|
});
|
|
|
|
it('a rejected confirm keeps the modal open and surfaces the error message', async () => {
|
|
const state = {
|
|
importOpen: true as boolean,
|
|
importDoc: makeDoc() as any,
|
|
importPreview: { total: 1, conflicts: [], unknownCategories: [], warnings: [] } as any,
|
|
importResult: null as any,
|
|
importError: null as string | null,
|
|
importing: false,
|
|
statusMessage: null as string | null,
|
|
};
|
|
|
|
try {
|
|
throw { error: { message: 'Server said no' } };
|
|
} catch (err) {
|
|
state.importError = importErrorMessage(err, 'Failed to import challenges');
|
|
} finally {
|
|
state.importing = false;
|
|
}
|
|
|
|
expect(state.importOpen).toBe(true);
|
|
expect(state.importError).toBe('Server said no');
|
|
expect(state.importing).toBe(false);
|
|
});
|
|
});
|