UNPKG

@mdfriday/foundry

Version:

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

295 lines 14.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const walkway_1 = require("../vo/walkway"); const type_1 = require("../type"); // Mock dependencies jest.mock('@pkg/log', () => ({ getDomainLogger: () => ({ error: jest.fn(), info: jest.fn(), debug: jest.fn(), }), })); jest.mock('../vo/fileinfo', () => ({ newFileInfoWithMeta: jest.fn().mockImplementation((fileInfo, meta) => ({ ...fileInfo, fileName: meta.fileName, relativeFilename: meta.relativeFilename, component: meta.component, root: meta.root, open: meta.open, })), })); jest.mock('../vo/filemeta', () => ({ newFileMeta: jest.fn().mockImplementation((filename) => ({ fileName: jest.fn().mockReturnValue(filename), relativeFilename: jest.fn().mockReturnValue(filename), component: jest.fn().mockReturnValue(''), root: jest.fn().mockReturnValue(''), open: jest.fn(), setOpenFunc: jest.fn(), })), })); describe('Walkway Value Object', () => { let mockFs; let mockWalkCallback; let mockWalkFn; let walkway; beforeEach(() => { mockWalkFn = jest.fn(); mockWalkCallback = { walkFn: mockWalkFn, }; mockFs = { name: jest.fn().mockReturnValue('MockFs'), 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('constructor', () => { it('should initialize with filesystem and callback', () => { walkway = new walkway_1.Walkway(mockFs, mockWalkCallback); expect(walkway).toBeDefined(); expect(walkway.fs).toBe(mockFs); expect(walkway.cb).toBe(mockWalkCallback); expect(walkway.root).toBe(''); expect(walkway.walked).toBe(false); }); it('should throw error when fs is not provided', () => { expect(() => new walkway_1.Walkway(null, mockWalkCallback)).toThrow('fs must be set'); }); it('should throw error when walkFn is not provided', () => { const invalidCallback = {}; expect(() => new walkway_1.Walkway(mockFs, invalidCallback)).toThrow('walkFn must be set'); }); it('should throw error when callback is not provided', () => { expect(() => new walkway_1.Walkway(mockFs, null)).toThrow('Cannot read properties of null'); }); }); describe('walkWith', () => { beforeEach(() => { walkway = new walkway_1.Walkway(mockFs, mockWalkCallback); }); it('should set root and config before walking', async () => { const config = { sortDirEntries: true }; const mockFileInfo = createMockFileInfo('test.txt', false); mockFs.stat.mockResolvedValue(mockFileInfo); await walkway.walkWith('/test/root', config); expect(walkway.root).toBe('/test/root'); expect(walkway.cfg).toBe(config); expect(mockFs.stat).toHaveBeenCalledWith('/test/root'); }); it('should handle empty root path', async () => { const mockFileInfo = createMockFileInfo('', true); mockFs.stat.mockResolvedValue(mockFileInfo); const mockFile = createMockFile([]); mockFs.open.mockResolvedValue(mockFile); await walkway.walkWith('', {}); expect(mockFs.stat).toHaveBeenCalledWith(''); }); }); describe('walk', () => { beforeEach(() => { walkway = new walkway_1.Walkway(mockFs, mockWalkCallback); }); it('should throw error if already walked', async () => { const mockFileInfo = createMockFileInfo('test.txt', false); mockFs.stat.mockResolvedValue(mockFileInfo); // First walk await walkway.walkWith('/test', {}); // Second walk should fail await expect(walkway.walk()).rejects.toThrow('this walkway is already walked'); }); it('should walk single file', async () => { const mockFileInfo = createMockFileInfo('test.txt', false); mockFs.stat.mockResolvedValue(mockFileInfo); await walkway.walkWith('/test.txt', {}); expect(mockFs.stat).toHaveBeenCalledWith('/test.txt'); expect(mockWalkFn).toHaveBeenCalledWith('/test.txt', expect.any(Object)); }); it('should walk directory with files', async () => { const mockDirInfo = createMockFileInfo('testdir', true); const mockFileInfo1 = createMockFileInfo('file1.txt', false); const mockFileInfo2 = createMockFileInfo('file2.txt', false); mockFs.stat .mockResolvedValueOnce(mockDirInfo) // Directory stat .mockResolvedValueOnce(mockFileInfo1) // file1.txt stat .mockResolvedValueOnce(mockFileInfo2); // file2.txt stat const mockFile = createMockFile([mockFileInfo1, mockFileInfo2]); mockFs.open.mockResolvedValue(mockFile); await walkway.walkWith('/testdir', {}); expect(mockWalkFn).toHaveBeenCalledTimes(3); // Directory + 2 files expect(mockWalkFn).toHaveBeenCalledWith('/testdir', expect.any(Object)); expect(mockWalkFn).toHaveBeenCalledWith('/testdir/file1.txt', expect.any(Object)); expect(mockWalkFn).toHaveBeenCalledWith('/testdir/file2.txt', expect.any(Object)); }); it('should sort directory entries when configured', async () => { const mockDirInfo = createMockFileInfo('testdir', true); const mockFileInfo1 = createMockFileInfo('zebra.txt', false); const mockFileInfo2 = createMockFileInfo('alpha.txt', false); mockFs.stat .mockResolvedValueOnce(mockDirInfo) .mockResolvedValueOnce(mockFileInfo2) // alpha.txt (sorted first) .mockResolvedValueOnce(mockFileInfo1); // zebra.txt (sorted second) const mockFile = createMockFile([mockFileInfo1, mockFileInfo2]); // Unsorted order mockFs.open.mockResolvedValue(mockFile); await walkway.walkWith('/testdir', { sortDirEntries: true }); expect(mockWalkFn).toHaveBeenCalledWith('/testdir/alpha.txt', expect.any(Object)); expect(mockWalkFn).toHaveBeenCalledWith('/testdir/zebra.txt', expect.any(Object)); }); it('should handle ErrSkipDir for directories', async () => { const mockDirInfo = createMockFileInfo('skipdir', true); mockFs.stat.mockResolvedValue(mockDirInfo); mockWalkFn.mockImplementation((path, info) => { if (info.isDir && info.isDir()) { throw type_1.ErrSkipDir; } }); await walkway.walkWith('/skipdir', {}); expect(mockWalkFn).toHaveBeenCalledTimes(1); expect(mockFs.open).not.toHaveBeenCalled(); // Should not read directory contents }); it('should handle filesystem stat errors', async () => { mockFs.stat.mockRejectedValue(new Error('File not found')); await expect(walkway.walkWith('/nonexistent', {})).rejects.toThrow('walk: stat: File not found'); }); it('should handle filesystem readdir errors', async () => { const mockDirInfo = createMockFileInfo('testdir', true); mockFs.stat.mockResolvedValue(mockDirInfo); mockFs.open.mockRejectedValue(new Error('Cannot read directory')); await expect(walkway.walkWith('/testdir', {})).rejects.toThrow('walk: readdir: Cannot read directory'); }); it('should handle walkFn errors', async () => { const mockFileInfo = createMockFileInfo('test.txt', false); mockFs.stat.mockResolvedValue(mockFileInfo); mockWalkFn.mockRejectedValue(new Error('Walk function failed')); await expect(walkway.walkWith('/test.txt', {})).rejects.toThrow('Walk function failed'); }); it('should handle nested directories', async () => { const mockRootDir = createMockFileInfo('root', true); const mockSubDir = createMockFileInfo('subdir', true); const mockFile = createMockFileInfo('file.txt', false); mockFs.stat .mockResolvedValueOnce(mockRootDir) // /root .mockResolvedValueOnce(mockSubDir) // /root/subdir .mockResolvedValueOnce(mockFile); // /root/subdir/file.txt const mockRootFile = createMockFile([mockSubDir]); const mockSubFile = createMockFile([mockFile]); const mockLeafFile = createMockFile([]); mockFs.open .mockResolvedValueOnce(mockRootFile) // Open /root .mockResolvedValueOnce(mockSubFile) // Open /root/subdir .mockResolvedValueOnce(mockLeafFile); // Open /root/subdir/file.txt (if needed) await walkway.walkWith('/root', {}); expect(mockWalkFn).toHaveBeenCalledTimes(3); expect(mockWalkFn).toHaveBeenCalledWith('/root', expect.any(Object)); expect(mockWalkFn).toHaveBeenCalledWith('/root/subdir', expect.any(Object)); expect(mockWalkFn).toHaveBeenCalledWith('/root/subdir/file.txt', expect.any(Object)); }); }); describe('error handling', () => { beforeEach(() => { walkway = new walkway_1.Walkway(mockFs, mockWalkCallback); }); it('should handle checkErr method for specific errors', async () => { const mockFileInfo = createMockFileInfo('test.txt', false); mockFs.stat.mockResolvedValue(mockFileInfo); // Mock checkErr to return true (ignore error) jest.spyOn(walkway, 'checkErr').mockReturnValue(true); // This should not throw despite the stat error mockFs.stat.mockRejectedValueOnce(new Error('Ignored error')); // The walk should complete without throwing await walkway.walkWith('/test.txt', {}); }); it('should handle path joining correctly', () => { // Since joinPath is not a public method, we'll test path joining through walkWith const mockFileInfo = createMockFileInfo('test.txt', false); mockFs.stat.mockResolvedValue(mockFileInfo); expect(async () => await walkway.walkWith('/root/file.txt', {})).not.toThrow(); }); it('should handle root path joining', () => { // Test with root path const mockFileInfo = createMockFileInfo('file.txt', false); mockFs.stat.mockResolvedValue(mockFileInfo); expect(async () => await walkway.walkWith('/', {})).not.toThrow(); }); it('should handle empty path components', () => { // Test with empty path const mockFileInfo = createMockFileInfo('', false); mockFs.stat.mockResolvedValue(mockFileInfo); expect(async () => await walkway.walkWith('', {})).not.toThrow(); }); }); describe('configuration options', () => { beforeEach(() => { walkway = new walkway_1.Walkway(mockFs, mockWalkCallback); }); it('should respect sortDirEntries option', async () => { const mockDirInfo = createMockFileInfo('testdir', true); const entries = [ createMockFileInfo('c.txt', false), createMockFileInfo('a.txt', false), createMockFileInfo('b.txt', false), ]; mockFs.stat .mockResolvedValueOnce(mockDirInfo) .mockResolvedValueOnce(entries[1]) // a.txt .mockResolvedValueOnce(entries[2]) // b.txt .mockResolvedValueOnce(entries[0]); // c.txt const mockFile = createMockFile(entries); mockFs.open.mockResolvedValue(mockFile); await walkway.walkWith('/testdir', { sortDirEntries: true }); // Verify the order of calls (after directory itself) expect(mockWalkFn).toHaveBeenNthCalledWith(2, '/testdir/a.txt', expect.any(Object)); expect(mockWalkFn).toHaveBeenNthCalledWith(3, '/testdir/b.txt', expect.any(Object)); expect(mockWalkFn).toHaveBeenNthCalledWith(4, '/testdir/c.txt', expect.any(Object)); }); }); // Helper functions function createMockFileInfo(name, isDir) { return { name: jest.fn().mockReturnValue(name), size: jest.fn().mockReturnValue(isDir ? 0 : 100), mode: jest.fn().mockReturnValue(isDir ? 0o755 : 0o644), modTime: jest.fn().mockReturnValue(new Date()), isDir: jest.fn().mockReturnValue(isDir), sys: jest.fn().mockReturnValue(null), fileName: jest.fn().mockReturnValue(`/${name}`), relativeFilename: jest.fn().mockReturnValue(name), component: jest.fn().mockReturnValue(''), root: jest.fn().mockReturnValue(''), open: jest.fn(), }; } function createMockFile(entries) { return { close: jest.fn(), read: jest.fn(), readAt: jest.fn(), seek: jest.fn(), write: jest.fn(), writeAt: jest.fn(), name: jest.fn(), readdir: jest.fn().mockResolvedValue(entries), readdirnames: jest.fn(), stat: jest.fn(), sync: jest.fn(), truncate: jest.fn(), writeString: jest.fn(), }; } }); //# sourceMappingURL=vo-walkway.test.js.map