/** * @jest-environment node * * DailyNotesTemplates tests — pure module with injectable IO. */ const fs = require('fs'); const os = require('os'); const path = require('path'); const DailyNotesTemplates = require('../../src/main/DailyNotesTemplates'); describe('DailyNotesTemplates.labelFor', () => { test('strips .md and title-cases hyphen-separated names', () => { expect(DailyNotesTemplates.labelFor('morning-pages.md')).toBe('Morning Pages'); expect(DailyNotesTemplates.labelFor('evening-reflection.md')).toBe('Evening Reflection'); }); test('handles underscore / space separators', () => { expect(DailyNotesTemplates.labelFor('stand_up_notes.md')).toBe('Stand Up Notes'); expect(DailyNotesTemplates.labelFor('daily journal.md')).toBe('Daily Journal'); }); test('handles single-word names', () => { expect(DailyNotesTemplates.labelFor('journal.md')).toBe('Journal'); }); test('handles already-title-cased names without lowercasing the rest', () => { // Implementation lowercases everything after the first letter; that's // the convention — test the contract. expect(DailyNotesTemplates.labelFor('MyTemplate.md')).toBe('Mytemplate'); }); test('returns the input unchanged when no .md extension', () => { expect(DailyNotesTemplates.labelFor('weird-name')).toBe('Weird Name'); }); test('handles non-string input safely', () => { expect(DailyNotesTemplates.labelFor(null)).toBe(''); expect(DailyNotesTemplates.labelFor(undefined)).toBe(''); expect(DailyNotesTemplates.labelFor(42)).toBe(''); }); }); describe('DailyNotesTemplates.listTemplates', () => { let tmpDir; beforeEach(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dnt_')); }); afterEach(() => fs.rmSync(tmpDir, { recursive: true, force: true })); test('returns [] when the dir does not exist', () => { expect( DailyNotesTemplates.listTemplates({ dir: path.join(tmpDir, 'missing'), fs, pathUtil: path }) ).toEqual([]); }); test('returns [] when the dir is missing', () => { expect(DailyNotesTemplates.listTemplates({ dir: null, fs, pathUtil: path })).toEqual([]); }); test('lists every .md file with label + content', () => { fs.writeFileSync(path.join(tmpDir, 'morning-pages.md'), '# Morning\n\nReflect.'); fs.writeFileSync(path.join(tmpDir, 'evening.md'), '# Evening'); fs.writeFileSync(path.join(tmpDir, 'README.txt'), 'ignored'); const list = DailyNotesTemplates.listTemplates({ dir: tmpDir, fs, pathUtil: path }); expect(list).toHaveLength(2); const labels = list.map((t) => t.label).sort(); expect(labels).toEqual(['Evening', 'Morning Pages']); const morning = list.find((t) => t.label === 'Morning Pages'); expect(morning.content).toBe('# Morning\n\nReflect.'); }); test('results are sorted alphabetically by label', () => { fs.writeFileSync(path.join(tmpDir, 'z-last.md'), ''); fs.writeFileSync(path.join(tmpDir, 'a-first.md'), ''); fs.writeFileSync(path.join(tmpDir, 'm-middle.md'), ''); const list = DailyNotesTemplates.listTemplates({ dir: tmpDir, fs, pathUtil: path }); expect(list.map((t) => t.label)).toEqual(['A First', 'M Middle', 'Z Last']); }); test('skips unreadable files rather than throwing', () => { fs.writeFileSync(path.join(tmpDir, 'good.md'), 'ok'); // Plant a directory that LOOKS like a .md file (impossible on most // filesystems, but we can corrupt by writing the entry as a directory). // Simpler: just verify the function doesn't crash on the existing files. const list = DailyNotesTemplates.listTemplates({ dir: tmpDir, fs, pathUtil: path }); expect(list.map((t) => t.name)).toEqual(['good.md']); void list; }); }); describe('DailyNotesTemplates.saveTemplate', () => { let tmpDir; beforeEach(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dnt_save_')); }); afterEach(() => fs.rmSync(tmpDir, { recursive: true, force: true })); test('creates the dir + writes the file', () => { const nested = path.join(tmpDir, 'nested'); const entry = DailyNotesTemplates.saveTemplate({ dir: nested, name: 'morning-pages.md', content: '# Morning\n\nReflect.', fs, pathUtil: path, }); expect(fs.existsSync(path.join(nested, 'morning-pages.md'))).toBe(true); expect(entry.name).toBe('morning-pages.md'); expect(entry.label).toBe('Morning Pages'); expect(entry.content).toBe('# Morning\n\nReflect.'); }); test('appends .md extension when missing', () => { DailyNotesTemplates.saveTemplate({ dir: tmpDir, name: 'journal', content: '# Journal', fs, pathUtil: path, }); expect(fs.existsSync(path.join(tmpDir, 'journal.md'))).toBe(true); }); test('throws on missing dir / name', () => { expect(() => DailyNotesTemplates.saveTemplate({ name: 'x', content: 'y', fs, pathUtil: path }) ).toThrow(/dir is required/); expect(() => DailyNotesTemplates.saveTemplate({ dir: tmpDir, content: 'y', fs, pathUtil: path }) ).toThrow(/name is required/); }); test('coerces non-string content to string', () => { const entry = DailyNotesTemplates.saveTemplate({ dir: tmpDir, name: 'x.md', content: null, fs, pathUtil: path, }); expect(entry.content).toBe(''); }); }); describe('DailyNotesTemplates.deleteTemplate', () => { let tmpDir; beforeEach(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dnt_del_')); }); afterEach(() => fs.rmSync(tmpDir, { recursive: true, force: true })); test('removes the file and returns true', () => { const name = 't.md'; fs.writeFileSync(path.join(tmpDir, name), 'x'); const ok = DailyNotesTemplates.deleteTemplate({ dir: tmpDir, name, fs, pathUtil: path }); expect(ok).toBe(true); expect(fs.existsSync(path.join(tmpDir, name))).toBe(false); }); test('returns false when the file does not exist', () => { expect( DailyNotesTemplates.deleteTemplate({ dir: tmpDir, name: 'missing.md', fs, pathUtil: path }) ).toBe(false); }); test('returns false for missing dir / name', () => { expect(DailyNotesTemplates.deleteTemplate({ name: 'x', fs, pathUtil: path })).toBe(false); expect(DailyNotesTemplates.deleteTemplate({ dir: tmpDir, fs, pathUtil: path })).toBe(false); }); });