mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-24 07:20:16 +05:30
feat(export): add save/select/delete export presets
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* Tests for the export-preset persistence module (Task 21).
|
||||
* The module is pure list logic over the settings store's `exportPresets`
|
||||
* array; the store is injected, mirroring the SettingsStore test pattern.
|
||||
*/
|
||||
|
||||
const ExportPresets = require('../../src/main/ExportPresets');
|
||||
|
||||
function createStore(initial = {}) {
|
||||
const data = { ...initial };
|
||||
return {
|
||||
get: (key, defaultValue) => (data[key] === undefined ? defaultValue : data[key]),
|
||||
set: (key, value) => {
|
||||
data[key] = value;
|
||||
},
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
describe('ExportPresets', () => {
|
||||
describe('loadPresets', () => {
|
||||
test('returns an empty array when nothing is stored', () => {
|
||||
expect(ExportPresets.loadPresets(createStore())).toEqual([]);
|
||||
});
|
||||
|
||||
test('returns the stored presets', () => {
|
||||
const store = createStore({
|
||||
exportPresets: [{ id: 'p1', name: 'Book PDF', format: 'pdf', options: { toc: true } }],
|
||||
});
|
||||
const presets = ExportPresets.loadPresets(store);
|
||||
expect(presets).toHaveLength(1);
|
||||
expect(presets[0].name).toBe('Book PDF');
|
||||
});
|
||||
|
||||
test('returns an empty array when the stored value is corrupt (not an array)', () => {
|
||||
expect(ExportPresets.loadPresets(createStore({ exportPresets: 'garbage' }))).toEqual([]);
|
||||
expect(ExportPresets.loadPresets(createStore({ exportPresets: { p1: 1 } }))).toEqual([]);
|
||||
});
|
||||
|
||||
test('drops malformed entries (missing id or name) from the stored array', () => {
|
||||
const store = createStore({
|
||||
exportPresets: [
|
||||
{ id: 'p1', name: 'Good', format: 'pdf', options: {} },
|
||||
{ name: 'No id', options: {} },
|
||||
{ id: 'p3', options: {} },
|
||||
null,
|
||||
],
|
||||
});
|
||||
const presets = ExportPresets.loadPresets(store);
|
||||
expect(presets).toHaveLength(1);
|
||||
expect(presets[0].id).toBe('p1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('savePreset', () => {
|
||||
test('adds a new preset with a generated id and persists it', () => {
|
||||
const store = createStore();
|
||||
const presets = ExportPresets.savePreset(store, {
|
||||
name: 'Book PDF',
|
||||
format: 'pdf',
|
||||
options: { toc: true, tocDepth: 3 },
|
||||
});
|
||||
|
||||
expect(presets).toHaveLength(1);
|
||||
expect(presets[0].id).toMatch(/^preset-/);
|
||||
expect(presets[0].name).toBe('Book PDF');
|
||||
expect(presets[0].format).toBe('pdf');
|
||||
expect(presets[0].options).toEqual({ toc: true, tocDepth: 3 });
|
||||
expect(store.data.exportPresets).toEqual(presets);
|
||||
});
|
||||
|
||||
test('upserts by id — saving with an existing id replaces the entry', () => {
|
||||
const store = createStore({
|
||||
exportPresets: [{ id: 'p1', name: 'Old name', format: 'pdf', options: { toc: false } }],
|
||||
});
|
||||
const presets = ExportPresets.savePreset(store, {
|
||||
id: 'p1',
|
||||
name: 'New name',
|
||||
format: 'docx',
|
||||
options: { toc: true },
|
||||
});
|
||||
|
||||
expect(presets).toHaveLength(1);
|
||||
expect(presets[0]).toEqual({
|
||||
id: 'p1',
|
||||
name: 'New name',
|
||||
format: 'docx',
|
||||
options: { toc: true },
|
||||
});
|
||||
expect(store.data.exportPresets).toEqual(presets);
|
||||
});
|
||||
|
||||
test('appends when the id is new, preserving existing presets', () => {
|
||||
const store = createStore({
|
||||
exportPresets: [{ id: 'p1', name: 'First', format: 'pdf', options: {} }],
|
||||
});
|
||||
const presets = ExportPresets.savePreset(store, {
|
||||
name: 'Second',
|
||||
format: 'html',
|
||||
options: {},
|
||||
});
|
||||
|
||||
expect(presets).toHaveLength(2);
|
||||
expect(presets.map((p) => p.name)).toEqual(['First', 'Second']);
|
||||
});
|
||||
|
||||
test('throws when the preset is not an object', () => {
|
||||
const store = createStore();
|
||||
expect(() => ExportPresets.savePreset(store, null)).toThrow('Preset must be an object');
|
||||
expect(() => ExportPresets.savePreset(store, 'nope')).toThrow('Preset must be an object');
|
||||
});
|
||||
|
||||
test('throws when the name is missing or empty after trimming', () => {
|
||||
const store = createStore();
|
||||
expect(() => ExportPresets.savePreset(store, { name: ' ', options: {} })).toThrow(
|
||||
'Preset name is required'
|
||||
);
|
||||
expect(() => ExportPresets.savePreset(store, { options: {} })).toThrow(
|
||||
'Preset name is required'
|
||||
);
|
||||
});
|
||||
|
||||
test('trims the name and caps it at 100 characters', () => {
|
||||
const store = createStore();
|
||||
const presets = ExportPresets.savePreset(store, { name: ' Spaced ', options: {} });
|
||||
expect(presets[0].name).toBe('Spaced');
|
||||
|
||||
const long = ExportPresets.savePreset(store, { name: 'x'.repeat(150), options: {} });
|
||||
expect(long[1].name).toHaveLength(100);
|
||||
});
|
||||
|
||||
test('defaults a missing options object to {} and a missing format to null', () => {
|
||||
const store = createStore();
|
||||
const presets = ExportPresets.savePreset(store, { name: 'Bare' });
|
||||
expect(presets[0].options).toEqual({});
|
||||
expect(presets[0].format).toBeNull();
|
||||
});
|
||||
|
||||
test('refuses to add beyond the preset cap', () => {
|
||||
const full = Array.from({ length: ExportPresets.MAX_PRESETS }, (_, i) => ({
|
||||
id: `p${i}`,
|
||||
name: `Preset ${i}`,
|
||||
format: 'pdf',
|
||||
options: {},
|
||||
}));
|
||||
const store = createStore({ exportPresets: full });
|
||||
expect(() => ExportPresets.savePreset(store, { name: 'One too many' })).toThrow(
|
||||
/more than \d+ export presets/
|
||||
);
|
||||
});
|
||||
|
||||
test('still allows updating an existing preset when the list is full', () => {
|
||||
const full = Array.from({ length: ExportPresets.MAX_PRESETS }, (_, i) => ({
|
||||
id: `p${i}`,
|
||||
name: `Preset ${i}`,
|
||||
format: 'pdf',
|
||||
options: {},
|
||||
}));
|
||||
const store = createStore({ exportPresets: full });
|
||||
const presets = ExportPresets.savePreset(store, { id: 'p7', name: 'Updated' });
|
||||
expect(presets).toHaveLength(ExportPresets.MAX_PRESETS);
|
||||
expect(presets.find((p) => p.id === 'p7').name).toBe('Updated');
|
||||
});
|
||||
|
||||
test('generates distinct ids for successive new presets', () => {
|
||||
const store = createStore();
|
||||
const a = ExportPresets.savePreset(store, { name: 'A' });
|
||||
const b = ExportPresets.savePreset(store, { name: 'B' });
|
||||
expect(a[0].id).not.toBe(b[1].id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deletePreset', () => {
|
||||
test('removes the preset with the given id and returns the updated list', () => {
|
||||
const store = createStore({
|
||||
exportPresets: [
|
||||
{ id: 'p1', name: 'Keep', format: 'pdf', options: {} },
|
||||
{ id: 'p2', name: 'Drop', format: 'html', options: {} },
|
||||
],
|
||||
});
|
||||
const presets = ExportPresets.deletePreset(store, 'p2');
|
||||
expect(presets).toHaveLength(1);
|
||||
expect(presets[0].id).toBe('p1');
|
||||
expect(store.data.exportPresets).toEqual(presets);
|
||||
});
|
||||
|
||||
test('is idempotent when the id does not exist', () => {
|
||||
const store = createStore({
|
||||
exportPresets: [{ id: 'p1', name: 'Keep', format: 'pdf', options: {} }],
|
||||
});
|
||||
const presets = ExportPresets.deletePreset(store, 'missing');
|
||||
expect(presets).toHaveLength(1);
|
||||
expect(store.data.exportPresets).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user