mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-06 16:04:23 +00:00
Add tests for remarkWikiLinks functionality
This commit is contained in:
337
app/src/utils/remarkWikiLinks.test.ts
Normal file
337
app/src/utils/remarkWikiLinks.test.ts
Normal file
@@ -0,0 +1,337 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { unified } from 'unified';
|
||||
import remarkParse from 'remark-parse';
|
||||
import remarkStringify from 'remark-stringify';
|
||||
import { remarkWikiLinks } from './remarkWikiLinks';
|
||||
import * as fileApi from '@/api/file';
|
||||
|
||||
// Mock the file API
|
||||
vi.mock('@/api/file');
|
||||
|
||||
// Mock window.API_BASE_URL
|
||||
const mockApiBaseUrl = 'http://localhost:8080/api/v1';
|
||||
|
||||
describe('remarkWikiLinks', () => {
|
||||
beforeEach(() => {
|
||||
window.API_BASE_URL = mockApiBaseUrl;
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
const createProcessor = (workspaceName: string) => {
|
||||
return unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkWikiLinks, workspaceName)
|
||||
.use(remarkStringify);
|
||||
};
|
||||
|
||||
describe('basic wiki link processing', () => {
|
||||
it('converts existing file links correctly', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName.mockResolvedValue(['docs/test.md']);
|
||||
|
||||
const processor = createProcessor('test-workspace');
|
||||
const markdown = 'Check out [[test]] for more info.';
|
||||
|
||||
const result = await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledWith(
|
||||
'test-workspace',
|
||||
'test.md'
|
||||
);
|
||||
expect(result.toString()).toContain('test');
|
||||
});
|
||||
|
||||
it('handles non-existent files with not found links', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName.mockResolvedValue([]);
|
||||
|
||||
const processor = createProcessor('test-workspace');
|
||||
const markdown = 'This [[nonexistent]] file does not exist.';
|
||||
|
||||
const result = await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledWith(
|
||||
'test-workspace',
|
||||
'nonexistent.md'
|
||||
);
|
||||
expect(result.toString()).toContain('nonexistent');
|
||||
});
|
||||
|
||||
it('handles API errors gracefully', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName.mockRejectedValue(new Error('API Error'));
|
||||
|
||||
const processor = createProcessor('test-workspace');
|
||||
const markdown = 'This [[error-file]] causes an error.';
|
||||
|
||||
// Should not throw
|
||||
const result = await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledWith(
|
||||
'test-workspace',
|
||||
'error-file.md'
|
||||
);
|
||||
expect(result.toString()).toContain('error-file');
|
||||
});
|
||||
});
|
||||
|
||||
describe('wiki link syntax variations', () => {
|
||||
it('handles basic wiki links', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName.mockResolvedValue(['basic.md']);
|
||||
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = '[[basic]]';
|
||||
|
||||
await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledWith(
|
||||
'workspace',
|
||||
'basic.md'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles wiki links with display text', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName.mockResolvedValue(['file.md']);
|
||||
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = '[[file|Display Text]]';
|
||||
|
||||
await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledWith('workspace', 'file.md');
|
||||
});
|
||||
|
||||
it('handles wiki links with headings', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName.mockResolvedValue(['file.md']);
|
||||
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = '[[file#section]]';
|
||||
|
||||
await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledWith('workspace', 'file.md');
|
||||
});
|
||||
|
||||
it('handles wiki links with both headings and display text', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName.mockResolvedValue(['file.md']);
|
||||
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = '[[file#section|Custom Display]]';
|
||||
|
||||
await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledWith('workspace', 'file.md');
|
||||
});
|
||||
|
||||
it('handles image wiki links', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName.mockResolvedValue(['image.png']);
|
||||
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = '![[image.png]]';
|
||||
|
||||
await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledWith(
|
||||
'workspace',
|
||||
'image.png'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles image wiki links with alt text', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName.mockResolvedValue(['photo.jpg']);
|
||||
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = '![[photo.jpg|Alt text for photo]]';
|
||||
|
||||
await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledWith(
|
||||
'workspace',
|
||||
'photo.jpg'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('file extension handling', () => {
|
||||
it('adds .md extension to files without extensions', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName.mockResolvedValue(['notes.md']);
|
||||
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = '[[notes]]';
|
||||
|
||||
await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledWith(
|
||||
'workspace',
|
||||
'notes.md'
|
||||
);
|
||||
});
|
||||
|
||||
it('preserves existing file extensions', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName.mockResolvedValue(['document.txt']);
|
||||
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = '[[document.txt]]';
|
||||
|
||||
await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledWith(
|
||||
'workspace',
|
||||
'document.txt'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles image files without adding .md extension', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName.mockResolvedValue(['screenshot.png']);
|
||||
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = '![[screenshot.png]]';
|
||||
|
||||
await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledWith(
|
||||
'workspace',
|
||||
'screenshot.png'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('multiple wiki links', () => {
|
||||
it('processes multiple wiki links in the same text', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName
|
||||
.mockResolvedValueOnce(['first.md'])
|
||||
.mockResolvedValueOnce(['second.md']);
|
||||
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = 'See [[first]] and [[second]] for details.';
|
||||
|
||||
await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledTimes(2);
|
||||
expect(mockLookupFileByName).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'workspace',
|
||||
'first.md'
|
||||
);
|
||||
expect(mockLookupFileByName).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'workspace',
|
||||
'second.md'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles mix of existing and non-existing files', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName
|
||||
.mockResolvedValueOnce(['exists.md'])
|
||||
.mockResolvedValueOnce([]);
|
||||
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = 'Check [[exists]] but not [[missing]].';
|
||||
|
||||
await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledTimes(2);
|
||||
expect(mockLookupFileByName).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'workspace',
|
||||
'exists.md'
|
||||
);
|
||||
expect(mockLookupFileByName).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'workspace',
|
||||
'missing.md'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('handles text without wiki links', async () => {
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = 'Just regular text with no wiki links.';
|
||||
|
||||
const result = await processor.process(markdown);
|
||||
|
||||
expect(result.toString()).toBe('Just regular text with no wiki links.\n');
|
||||
expect(fileApi.lookupFileByName).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handles wiki links with only spaces', async () => {
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = 'Spaces [[ ]] link.';
|
||||
|
||||
const result = await processor.process(markdown);
|
||||
|
||||
expect(result.toString()).toContain('Spaces');
|
||||
// Should not call API for empty/whitespace-only links
|
||||
});
|
||||
|
||||
it('handles nested brackets', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName.mockResolvedValue(['test.md']);
|
||||
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = '[[test]] and some [regular](link) text.';
|
||||
|
||||
await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledWith('workspace', 'test.md');
|
||||
});
|
||||
|
||||
it('handles special characters in file names', async () => {
|
||||
const mockLookupFileByName = vi.mocked(fileApi.lookupFileByName);
|
||||
mockLookupFileByName.mockResolvedValue(['file with spaces & symbols.md']);
|
||||
|
||||
const processor = createProcessor('workspace');
|
||||
const markdown = '[[file with spaces & symbols]]';
|
||||
|
||||
await processor.process(markdown);
|
||||
|
||||
expect(mockLookupFileByName).toHaveBeenCalledWith(
|
||||
'workspace',
|
||||
'file with spaces & symbols.md'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('workspace handling', () => {
|
||||
it('handles empty workspace name gracefully', async () => {
|
||||
const processor = createProcessor('');
|
||||
const markdown = '[[test]]';
|
||||
|
||||
const result = await processor.process(markdown);
|
||||
|
||||
expect(result.toString()).toContain('test');
|
||||
// Should not call API when workspace is empty
|
||||
expect(fileApi.lookupFileByName).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not process links when workspace is not provided', async () => {
|
||||
const processor = unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkWikiLinks, '')
|
||||
.use(remarkStringify);
|
||||
|
||||
const markdown = '[[test]]';
|
||||
|
||||
const result = await processor.process(markdown);
|
||||
|
||||
expect(result.toString()).toContain('test');
|
||||
expect(fileApi.lookupFileByName).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user