@mdfriday/foundry
Version:
The core engine of MDFriday. Convert Markdown and shortcodes into fully themed static sites – Hugo-style, powered by TypeScript.
403 lines • 18.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const pathparser_1 = require("../vo/pathparser");
const type_1 = require("../type");
// Create a global test processor instance
const testProcessor = new pathparser_1.PathProcessorImpl();
describe('PathParser', () => {
describe('Parse', () => {
const testCases = [
{
name: 'Basic Markdown file',
path: '/a/b/c.md',
assertions: (p) => {
expect(p.isContent()).toBe(true);
expect(p.isLeafBundle()).toBe(false);
expect(p.name()).toBe('c.md');
expect(p.base()).toBe('/a/b/c');
expect(p.section()).toBe('a');
expect(p.baseNameNoIdentifier()).toBe('c');
expect(p.path()).toBe('/a/b/c.md');
expect(p.dir()).toBe('/a/b');
expect(p.container()).toBe('b');
expect(p.containerDir()).toBe('/a/b');
expect(p.ext()).toBe('.md');
},
},
{
name: 'Basic text file, 1 space in dir',
path: '/a b/c.txt',
assertions: (p) => {
expect(p.base()).toBe('/a-b/c.txt');
},
},
{
name: 'Basic text file, 2 spaces in dir',
path: '/a b/c.txt',
assertions: (p) => {
expect(p.base()).toBe('/a--b/c.txt');
},
},
{
name: 'Basic text file, 1 space in filename',
path: '/a/b c.txt',
assertions: (p) => {
expect(p.base()).toBe('/a/b-c.txt');
},
},
{
name: 'Basic text file, 2 spaces in filename',
path: '/a/b c.txt',
assertions: (p) => {
expect(p.base()).toBe('/a/b--c.txt');
},
},
{
name: 'Basic md file, with index.md name',
path: '/a/index.md',
assertions: (p) => {
expect(p.base()).toBe('/a');
expect(p.isLeafBundle()).toBe(true);
expect(p.bundleType()).toBe(type_1.PathType.Leaf);
},
},
{
name: 'Basic md file, with _index.md name',
path: '/a/_index.md',
assertions: (p) => {
expect(p.base()).toBe('/a');
expect(p.isBranchBundle()).toBe(true);
expect(p.bundleType()).toBe(type_1.PathType.Branch);
},
},
{
name: 'Basic md file, with root _index.md name',
path: '/_index.md',
assertions: (p) => {
expect(p.base()).toBe('/');
expect(p.isBranchBundle()).toBe(true);
},
},
{
name: 'Basic text file, mixed case and spaces, unnormalized',
path: '/abc/Foo BAR.txt',
assertions: (p) => {
const pp = p.unnormalized();
expect(pp).not.toBeNull();
expect(pp.baseNameNoIdentifier()).toBe('Foo BAR');
expect(pp.section()).toBe('abc');
},
},
{
name: 'Content single page',
path: '/blog/my-post.md',
assertions: (p) => {
expect(p.bundleType()).toBe(type_1.PathType.ContentSingle);
expect(p.isContent()).toBe(true);
expect(p.isBundle()).toBe(false);
expect(p.section()).toBe('blog');
},
},
{
name: 'Nested leaf bundle',
path: '/blog/2023/my-post/index.md',
assertions: (p) => {
expect(p.bundleType()).toBe(type_1.PathType.Leaf);
expect(p.isLeafBundle()).toBe(true);
expect(p.container()).toBe('my-post');
expect(p.section()).toBe('blog');
},
},
{
name: 'Static resource',
path: '/images/logo.png',
assertions: (p) => {
expect(p.bundleType()).toBe(type_1.PathType.File);
expect(p.isContent()).toBe(false);
expect(p.ext()).toBe('.png');
},
},
{
name: 'HTML content file',
path: '/pages/about.html',
assertions: (p) => {
expect(p.bundleType()).toBe(type_1.PathType.ContentSingle);
expect(p.isContent()).toBe(true);
expect(p.isHTML()).toBe(true);
expect(p.ext()).toBe('.html');
},
},
{
name: 'Multi-language content',
path: '/blog/post.en.md',
assertions: (p) => {
expect(p.bundleType()).toBe(type_1.PathType.ContentSingle);
expect(p.isContent()).toBe(true);
expect(p.lang()).toBe('.en');
expect(p.ext()).toBe('.md');
},
},
];
testCases.forEach(({ name, path, assertions }) => {
test(name, () => {
const parsed = testProcessor.parse('content', path);
assertions(parsed);
});
});
});
// Add specific test cases for the nameNoIdentifier issue
describe('nameNoIdentifier issue verification', () => {
test('should return correct nameNoIdentifier for root _index.md', () => {
const path = testProcessor.parse('content', '/_index.md');
const nameNoId = path.nameNoIdentifier();
console.log('Root _index.md test:');
console.log(' Input path: /_index.md');
console.log(' nameNoIdentifier():', nameNoId);
console.log(' Expected: _index');
expect(nameNoId).toBe('_index');
});
test('should return correct nameNoIdentifier for nested _index.md', () => {
const path = testProcessor.parse('content', '/categories/Development/_index.md');
const nameNoId = path.nameNoIdentifier();
console.log('Nested _index.md test:');
console.log(' Input path: /categories/Development/_index.md');
console.log(' nameNoIdentifier():', nameNoId);
console.log(' Expected: _index');
expect(nameNoId).toBe('_index');
});
test('should return correct nameNoIdentifier for root _index.md', () => {
const path = testProcessor.parse('content', '/_index.md');
const nameNoId = path.nameNoIdentifier();
console.log('Root _index.md test:');
console.log(' Input path: /_index.md');
console.log(' nameNoIdentifier():', nameNoId);
console.log(' Expected: index');
expect(nameNoId).toBe('_index');
});
test('should return correct nameNoIdentifier for nested _index.md', () => {
const path = testProcessor.parse('content', '/blog/my-post/_index.md');
const nameNoId = path.nameNoIdentifier();
console.log('Nested _index.md test:');
console.log(' Input path: /blog/my-post/_index.md');
console.log(' nameNoIdentifier():', nameNoId);
console.log(' Expected: index');
expect(nameNoId).toBe('_index');
});
test('should return correct nameNoIdentifier for regular content files', () => {
const path = testProcessor.parse('content', '/blog/my-post.md');
const nameNoId = path.nameNoIdentifier();
console.log('Regular content file test:');
console.log(' Input path: /blog/my-post.md');
console.log(' nameNoIdentifier():', nameNoId);
console.log(' Expected: my-post');
expect(nameNoId).toBe('my-post');
});
// Additional edge cases
test('should handle paths with multiple identifiers correctly', () => {
const path = testProcessor.parse('content', '/blog/post.en.md');
const nameNoId = path.nameNoIdentifier();
console.log('Multi-identifier test:');
console.log(' Input path: /blog/post.en.md');
console.log(' nameNoIdentifier():', nameNoId);
console.log(' Expected: post');
expect(nameNoId).toBe('post');
});
});
describe('HasExt', () => {
test('should detect extensions correctly', () => {
expect(pathparser_1.PathParserUtils.hasExtension('/a/b/c.txt')).toBe(true);
expect(pathparser_1.PathParserUtils.hasExtension('/a/b.c/d.txt')).toBe(true);
expect(pathparser_1.PathParserUtils.hasExtension('/a/b/c')).toBe(false);
expect(pathparser_1.PathParserUtils.hasExtension('/a/b.c/d')).toBe(false);
});
});
describe('ParseIdentity', () => {
test('should parse identity correctly', () => {
const identity = testProcessor.parseIdentity('assets', '/a/b.css');
expect(identity.identifierBase()).toContain('b.css');
});
});
describe('ParseBaseAndBaseNameNoIdentifier', () => {
test('should parse base and base name without identifier', () => {
const [base, baseName] = testProcessor.parseBaseAndBaseNameNoIdentifier('content', '/blog/post.md');
expect(base).toBe('/blog/post');
expect(baseName).toBe('post');
});
});
describe('Component handling', () => {
test('should handle different components correctly', () => {
const contentPath = testProcessor.parse('content', '/blog/post.md');
const staticPath = testProcessor.parse('static', '/images/logo.png');
const archetypePath = testProcessor.parse('archetypes', '/post.md');
expect(contentPath.isContent()).toBe(true);
expect(staticPath.isContent()).toBe(false);
expect(archetypePath.isContent()).toBe(true);
});
});
describe('Bundle detection', () => {
test('should detect leaf bundles correctly', () => {
const leafBundle = testProcessor.parse('content', '/blog/my-post/index.md');
expect(leafBundle.isLeafBundle()).toBe(true);
expect(leafBundle.isBundle()).toBe(false);
expect(leafBundle.bundleType()).toBe(type_1.PathType.Leaf);
});
test('should detect branch bundles correctly', () => {
const branchBundle = testProcessor.parse('content', '/blog/_index.md');
expect(branchBundle.isBranchBundle()).toBe(true);
expect(branchBundle.isBundle()).toBe(true);
expect(branchBundle.bundleType()).toBe(type_1.PathType.Branch);
});
test('should handle section-level leaf bundles', () => {
const sectionLeaf = testProcessor.parse('content', '/blog/index.md');
expect(sectionLeaf.isLeafBundle()).toBe(true);
expect(sectionLeaf.section()).toBe('blog');
});
});
describe('Edge cases', () => {
test('should handle root paths', () => {
const rootIndex = testProcessor.parse('content', '/_index.md');
expect(rootIndex.section()).toBe('');
expect(rootIndex.isBranchBundle()).toBe(true);
});
test('should handle paths without extensions', () => {
const noExt = testProcessor.parse('content', '/blog/post');
expect(noExt.ext()).toBe('');
expect(noExt.isContent()).toBe(false);
});
test('should handle empty paths', () => {
const emptyPath = testProcessor.parse('content', '');
expect(emptyPath.path()).toBe('/');
});
test('should handle Windows-style paths', () => {
// Mock Windows platform
const originalPlatform = process.platform;
Object.defineProperty(process, 'platform', { value: 'win32' });
const windowsPath = testProcessor.parse('content', '\\blog\\post.md');
expect(windowsPath.path()).toBe('/blog/post.md');
// Restore original platform
Object.defineProperty(process, 'platform', { value: originalPlatform });
});
});
});
describe('BasicPathNormalizer', () => {
test('should normalize with default settings', () => {
const normalizer = new pathparser_1.BasicPathNormalizer();
expect(normalizer.normalize('BLOG/My Post.md')).toBe('blog/my-post.md');
});
test('should normalize with custom settings', () => {
const normalizer = new pathparser_1.BasicPathNormalizer(false, true);
expect(normalizer.normalize('BLOG/My Post.md')).toBe('BLOG/My-Post.md');
});
test('should handle spaces correctly', () => {
const normalizer = new pathparser_1.BasicPathNormalizer(true, true);
expect(normalizer.normalize('foo bar')).toBe('foo-bar');
expect(normalizer.normalize('foo\tbar')).toBe('foo-bar');
expect(normalizer.normalize('foo\nbar')).toBe('foo-bar');
});
});
describe('DefaultFileExtensionChecker', () => {
const checker = new pathparser_1.DefaultFileExtensionChecker();
test('should identify content extensions', () => {
expect(checker.isContentExt('.md')).toBe(true);
expect(checker.isContentExt('.markdown')).toBe(true);
expect(checker.isContentExt('.html')).toBe(true);
expect(checker.isContentExt('.txt')).toBe(false);
expect(checker.isContentExt('.png')).toBe(false);
});
test('should identify HTML extensions', () => {
expect(checker.isHTML('.html')).toBe(true);
expect(checker.isHTML('.htm')).toBe(true);
expect(checker.isHTML('.md')).toBe(false);
});
test('should handle case insensitive extensions', () => {
expect(checker.isContentExt('.MD')).toBe(true);
expect(checker.isHTML('.HTML')).toBe(true);
});
test('should detect extension presence', () => {
expect(checker.hasExt('/path/file.txt')).toBe(true);
expect(checker.hasExt('/path/file')).toBe(false);
expect(checker.hasExt('/path.dir/file')).toBe(false);
expect(checker.hasExt('/path.dir/file.txt')).toBe(true);
});
});
describe('PathParserUtils', () => {
describe('parseBasic', () => {
test('should parse basic path components', () => {
const result = pathparser_1.PathParserUtils.parseBasic('/blog/posts/hello-world.md');
expect(result).toEqual({
dir: '/blog/posts',
name: 'hello-world.md',
ext: '.md',
nameWithoutExt: 'hello-world'
});
});
test('should handle paths without directory', () => {
const result = pathparser_1.PathParserUtils.parseBasic('file.txt');
expect(result).toEqual({
dir: '',
name: 'file.txt',
ext: '.txt',
nameWithoutExt: 'file'
});
});
test('should handle paths without extension', () => {
const result = pathparser_1.PathParserUtils.parseBasic('/blog/posts/hello');
expect(result).toEqual({
dir: '/blog/posts',
name: 'hello',
ext: '',
nameWithoutExt: 'hello'
});
});
});
describe('join', () => {
test('should join path segments correctly', () => {
expect(pathparser_1.PathParserUtils.join('blog', 'posts', 'hello.md')).toBe('blog/posts/hello.md');
expect(pathparser_1.PathParserUtils.join('/blog/', '/posts/', '/hello.md/')).toBe('blog/posts/hello.md');
expect(pathparser_1.PathParserUtils.join('', 'blog', '', 'posts')).toBe('blog/posts');
});
});
describe('extractSection', () => {
test('should extract section correctly', () => {
expect(pathparser_1.PathParserUtils.extractSection('/blog/posts/hello.md')).toBe('blog');
expect(pathparser_1.PathParserUtils.extractSection('blog/posts/hello.md')).toBe('blog');
expect(pathparser_1.PathParserUtils.extractSection('/blog')).toBe('blog');
expect(pathparser_1.PathParserUtils.extractSection('blog')).toBe('blog');
});
});
describe('removeExtension', () => {
test('should remove extensions correctly', () => {
expect(pathparser_1.PathParserUtils.removeExtension('/blog/post.md')).toBe('/blog/post');
expect(pathparser_1.PathParserUtils.removeExtension('/blog/post')).toBe('/blog/post');
expect(pathparser_1.PathParserUtils.removeExtension('/path.dir/post.md')).toBe('/path.dir/post');
});
});
describe('isBundle', () => {
test('should detect bundle files', () => {
expect(pathparser_1.PathParserUtils.isBundle('/blog/index.md')).toBe(true);
expect(pathparser_1.PathParserUtils.isBundle('/blog/_index.md')).toBe(true);
expect(pathparser_1.PathParserUtils.isBundle('/blog/post.md')).toBe(false);
});
});
});
describe('Performance and Memory', () => {
test('should handle large number of paths efficiently', () => {
const start = performance.now();
for (let i = 0; i < 1000; i++) {
const path = testProcessor.parse('content', `/blog/post-${i}.md`);
expect(path.isContent()).toBe(true);
}
const end = performance.now();
expect(end - start).toBeLessThan(1000); // Should complete within 1 second
});
test('should not leak memory with unnormalized paths', () => {
const paths = [];
for (let i = 0; i < 100; i++) {
const path = testProcessor.parse('content', `/Blog/Post ${i}.md`);
paths.push(path.unnormalized());
}
// All unnormalized paths should have different identities but same references when possible
expect(paths.length).toBe(100);
});
});
//# sourceMappingURL=pathparser.test.js.map