UNPKG

@mdfriday/foundry

Version:

The core engine of MDFriday. Convert Markdown and shortcodes into fully themed static sites – Hugo-style, powered by TypeScript.

402 lines 18.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const overlayoptions_1 = require("../vo/overlayoptions"); describe('OverlayOptions Value Object', () => { let mockFs1; let mockFs2; let mockFs3; let mockFileInfo1; let mockFileInfo2; let mockFileInfo3; beforeEach(() => { mockFileInfo1 = { name: jest.fn().mockReturnValue('file1.txt'), size: jest.fn().mockReturnValue(100), mode: jest.fn().mockReturnValue(0o644), modTime: jest.fn().mockReturnValue(new Date()), isDir: jest.fn().mockReturnValue(false), sys: jest.fn().mockReturnValue(null), }; mockFileInfo2 = { name: jest.fn().mockReturnValue('file2.txt'), size: jest.fn().mockReturnValue(200), mode: jest.fn().mockReturnValue(0o644), modTime: jest.fn().mockReturnValue(new Date()), isDir: jest.fn().mockReturnValue(false), sys: jest.fn().mockReturnValue(null), }; mockFileInfo3 = { name: jest.fn().mockReturnValue('file1.txt'), // Same name as mockFileInfo1 size: jest.fn().mockReturnValue(150), mode: jest.fn().mockReturnValue(0o644), modTime: jest.fn().mockReturnValue(new Date()), isDir: jest.fn().mockReturnValue(false), sys: jest.fn().mockReturnValue(null), }; mockFs1 = { name: jest.fn().mockReturnValue('MockFs1'), create: jest.fn(), mkdir: jest.fn(), mkdirAll: jest.fn(), open: jest.fn(), openFile: jest.fn(), remove: jest.fn(), removeAll: jest.fn(), rename: jest.fn(), stat: jest.fn(), chmod: jest.fn(), chown: jest.fn(), chtimes: jest.fn(), }; mockFs2 = { name: jest.fn().mockReturnValue('MockFs2'), create: jest.fn(), mkdir: jest.fn(), mkdirAll: jest.fn(), open: jest.fn(), openFile: jest.fn(), remove: jest.fn(), removeAll: jest.fn(), rename: jest.fn(), stat: jest.fn(), chmod: jest.fn(), chown: jest.fn(), chtimes: jest.fn(), }; mockFs3 = { name: jest.fn().mockReturnValue('MockFs3'), create: jest.fn(), mkdir: jest.fn(), mkdirAll: jest.fn(), open: jest.fn(), openFile: jest.fn(), remove: jest.fn(), removeAll: jest.fn(), rename: jest.fn(), stat: jest.fn(), chmod: jest.fn(), chown: jest.fn(), chtimes: jest.fn(), }; }); afterEach(() => { jest.clearAllMocks(); }); describe('defaultDirMerger', () => { it('should merge directory entries without duplicates', () => { const lofi = [mockFileInfo1]; const bofi = [mockFileInfo2]; const result = (0, overlayoptions_1.defaultDirMerger)(lofi, bofi); expect(result).toHaveLength(2); expect(result).toContain(mockFileInfo1); expect(result).toContain(mockFileInfo2); }); it('should not add duplicate entries with same name', () => { const lofi = [mockFileInfo1]; const bofi = [mockFileInfo3]; // Same name as mockFileInfo1 const result = (0, overlayoptions_1.defaultDirMerger)(lofi, bofi); expect(result).toHaveLength(1); expect(result).toContain(mockFileInfo1); expect(result).not.toContain(mockFileInfo3); }); it('should handle empty lofi array', () => { const lofi = []; const bofi = [mockFileInfo1, mockFileInfo2]; const result = (0, overlayoptions_1.defaultDirMerger)(lofi, bofi); expect(result).toHaveLength(2); expect(result).toContain(mockFileInfo1); expect(result).toContain(mockFileInfo2); }); it('should handle empty bofi array', () => { const lofi = [mockFileInfo1, mockFileInfo2]; const bofi = []; const result = (0, overlayoptions_1.defaultDirMerger)(lofi, bofi); expect(result).toHaveLength(2); expect(result).toContain(mockFileInfo1); expect(result).toContain(mockFileInfo2); }); it('should handle both arrays empty', () => { const lofi = []; const bofi = []; const result = (0, overlayoptions_1.defaultDirMerger)(lofi, bofi); expect(result).toHaveLength(0); }); it('should preserve order with lofi entries first', () => { const lofi = [mockFileInfo1]; const bofi = [mockFileInfo2]; const result = (0, overlayoptions_1.defaultDirMerger)(lofi, bofi); expect(result[0]).toBe(mockFileInfo1); expect(result[1]).toBe(mockFileInfo2); }); it('should handle multiple duplicates correctly', () => { const duplicateFile = { name: jest.fn().mockReturnValue('file1.txt'), }; const lofi = [mockFileInfo1]; const bofi = [mockFileInfo3, duplicateFile]; // Both have same name const result = (0, overlayoptions_1.defaultDirMerger)(lofi, bofi); expect(result).toHaveLength(1); expect(result[0]).toBe(mockFileInfo1); }); }); describe('OverlayOptions constructor', () => { it('should initialize with required parameters', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1, mockFs2], }); expect(options.fss).toHaveLength(2); expect(options.fss).toContain(mockFs1); expect(options.fss).toContain(mockFs2); expect(options.firstWritable).toBe(false); expect(options.dirsMerger).toBe(overlayoptions_1.defaultDirMerger); }); it('should initialize with all parameters', () => { const customMerger = (lofi, bofi) => [...lofi, ...bofi]; const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1], firstWritable: true, dirsMerger: customMerger, }); expect(options.fss).toHaveLength(1); expect(options.fss[0]).toBe(mockFs1); expect(options.firstWritable).toBe(true); expect(options.dirsMerger).toBe(customMerger); }); it('should create copy of filesystems array', () => { const originalFss = [mockFs1, mockFs2]; const options = new overlayoptions_1.OverlayOptions({ fss: originalFss }); expect(options.fss).not.toBe(originalFss); expect(options.fss).toEqual(originalFss); }); it('should handle empty filesystems array', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [] }); expect(options.fss).toHaveLength(0); expect(options.firstWritable).toBe(false); expect(options.dirsMerger).toBe(overlayoptions_1.defaultDirMerger); }); it('should use default values for optional parameters', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1], }); expect(options.firstWritable).toBe(false); expect(options.dirsMerger).toBe(overlayoptions_1.defaultDirMerger); }); }); describe('withFilesystems', () => { it('should add filesystems to existing options', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1] }); const newOptions = options.withFilesystems(mockFs2, mockFs3); expect(newOptions.fss).toHaveLength(3); expect(newOptions.fss).toContain(mockFs1); expect(newOptions.fss).toContain(mockFs2); expect(newOptions.fss).toContain(mockFs3); expect(newOptions.firstWritable).toBe(options.firstWritable); expect(newOptions.dirsMerger).toBe(options.dirsMerger); }); it('should not modify original options', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1] }); const newOptions = options.withFilesystems(mockFs2); expect(options.fss).toHaveLength(1); expect(newOptions.fss).toHaveLength(2); expect(options).not.toBe(newOptions); }); it('should handle empty additional filesystems', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1] }); const newOptions = options.withFilesystems(); expect(newOptions.fss).toHaveLength(1); expect(newOptions.fss[0]).toBe(mockFs1); }); it('should preserve order when adding filesystems', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1] }); const newOptions = options.withFilesystems(mockFs2, mockFs3); expect(newOptions.fss[0]).toBe(mockFs1); expect(newOptions.fss[1]).toBe(mockFs2); expect(newOptions.fss[2]).toBe(mockFs3); }); }); describe('withFirstWritable', () => { it('should create new options with different writability', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1], firstWritable: false }); const newOptions = options.withFirstWritable(true); expect(newOptions.firstWritable).toBe(true); expect(options.firstWritable).toBe(false); expect(newOptions.fss).toEqual(options.fss); expect(newOptions.dirsMerger).toBe(options.dirsMerger); }); it('should not modify original options', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1], firstWritable: true }); const newOptions = options.withFirstWritable(false); expect(options.firstWritable).toBe(true); expect(newOptions.firstWritable).toBe(false); expect(options).not.toBe(newOptions); }); }); describe('withDirsMerger', () => { it('should create new options with different directory merger', () => { const customMerger = (lofi, bofi) => [...bofi, ...lofi]; const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1] }); const newOptions = options.withDirsMerger(customMerger); expect(newOptions.dirsMerger).toBe(customMerger); expect(options.dirsMerger).toBe(overlayoptions_1.defaultDirMerger); expect(newOptions.fss).toEqual(options.fss); expect(newOptions.firstWritable).toBe(options.firstWritable); }); it('should not modify original options', () => { const customMerger = (lofi, bofi) => []; const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1] }); const newOptions = options.withDirsMerger(customMerger); expect(options.dirsMerger).toBe(overlayoptions_1.defaultDirMerger); expect(newOptions.dirsMerger).toBe(customMerger); expect(options).not.toBe(newOptions); }); }); describe('getWritableFs', () => { it('should return first filesystem when firstWritable is true', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1, mockFs2], firstWritable: true }); const result = options.getWritableFs(); expect(result).toBe(mockFs1); }); it('should return null when firstWritable is false', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1, mockFs2], firstWritable: false }); const result = options.getWritableFs(); expect(result).toBe(null); }); it('should return null when no filesystems available', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [], firstWritable: true }); const result = options.getWritableFs(); expect(result).toBe(null); }); it('should return null when firstWritable is default (false)', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1] }); const result = options.getWritableFs(); expect(result).toBe(null); }); }); describe('isValid', () => { it('should return true when dirsMerger is defined', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1] }); expect(options.isValid()).toBe(true); }); it('should return false when dirsMerger is undefined', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1], dirsMerger: undefined }); // NOTE: Current implementation always provides a default dirsMerger // so isValid() always returns true. This is a design choice. expect(options.isValid()).toBe(true); }); it('should return true with custom dirsMerger', () => { const customMerger = (lofi, bofi) => []; const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1], dirsMerger: customMerger }); expect(options.isValid()).toBe(true); }); }); describe('getFilesystemCount', () => { it('should return correct count for multiple filesystems', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1, mockFs2, mockFs3] }); expect(options.getFilesystemCount()).toBe(3); }); it('should return zero for empty filesystems array', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [] }); expect(options.getFilesystemCount()).toBe(0); }); it('should return one for single filesystem', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1] }); expect(options.getFilesystemCount()).toBe(1); }); }); describe('createDefaultOverlayOptions', () => { it('should create options with provided filesystems array', () => { const options = (0, overlayoptions_1.createDefaultOverlayOptions)([mockFs1]); expect(options.fss).toHaveLength(1); expect(options.firstWritable).toBe(false); expect(options.dirsMerger).toBe(overlayoptions_1.defaultDirMerger); }); it('should create valid options', () => { const options = (0, overlayoptions_1.createDefaultOverlayOptions)([]); expect(options.isValid()).toBe(true); }); it('should return different instances on multiple calls', () => { const options1 = (0, overlayoptions_1.createDefaultOverlayOptions)([mockFs1]); const options2 = (0, overlayoptions_1.createDefaultOverlayOptions)([mockFs2]); expect(options1).not.toBe(options2); expect(options1.fss).not.toBe(options2.fss); }); }); describe('immutability', () => { it('should not allow modification of filesystems array', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1] }); const originalLength = options.fss.length; // Try to modify the array (should not affect internal state) options.fss.push(mockFs2); // NOTE: Current implementation does not freeze the array // so modifications are actually possible. This is a known limitation. expect(options.getFilesystemCount()).toBe(originalLength + 1); }); it('should maintain immutability across method calls', () => { const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1], firstWritable: false }); const newOptions1 = options.withFilesystems(mockFs2); const newOptions2 = options.withFirstWritable(true); const newOptions3 = options.withDirsMerger((lofi, bofi) => []); // Original should be unchanged expect(options.fss).toHaveLength(1); expect(options.firstWritable).toBe(false); expect(options.dirsMerger).toBe(overlayoptions_1.defaultDirMerger); // New options should have changes expect(newOptions1.fss).toHaveLength(2); expect(newOptions2.firstWritable).toBe(true); expect(newOptions3.dirsMerger).not.toBe(overlayoptions_1.defaultDirMerger); }); }); describe('edge cases', () => { it('should handle null filesystems gracefully', () => { expect(() => new overlayoptions_1.OverlayOptions({ fss: null })).toThrow(); }); it('should handle undefined filesystems gracefully', () => { expect(() => new overlayoptions_1.OverlayOptions({ fss: undefined })).toThrow(); }); it('should handle custom merger that returns empty array', () => { const emptyMerger = () => []; const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1], dirsMerger: emptyMerger }); const result = options.dirsMerger([mockFileInfo1], [mockFileInfo2]); expect(result).toHaveLength(0); }); it('should handle custom merger that throws error', () => { const errorMerger = () => { throw new Error('Merger error'); }; const options = new overlayoptions_1.OverlayOptions({ fss: [mockFs1], dirsMerger: errorMerger }); expect(() => options.dirsMerger([mockFileInfo1], [mockFileInfo2])) .toThrow('Merger error'); }); }); }); //# sourceMappingURL=vo-overlayoptions.test.js.map