Skip to main content
Glama

Scrapbox Cosense MCP Server

by worldnine
markdown-converter.test.ts10.2 kB
import { convertMarkdownToScrapbox } from '@/utils/markdown-converter.js'; // md2sbライブラリをモック jest.mock('md2sb', () => ({ default: jest.fn() })); describe('convertMarkdownToScrapbox', () => { let mockMd2sb: jest.Mock; beforeEach(() => { jest.clearAllMocks(); // md2sbモジュールのモック const md2sbModule = require('md2sb'); mockMd2sb = md2sbModule.default; }); describe('正常ケース', () => { test('基本的なMarkdownが変換されること', async () => { const markdown = '# Header\nContent'; const expectedScrapbox = '[* Header]\nContent'; mockMd2sb.mockResolvedValue(expectedScrapbox); const result = await convertMarkdownToScrapbox(markdown); expect(result).toBe(expectedScrapbox); expect(mockMd2sb).toHaveBeenCalledWith(markdown); expect(mockMd2sb).toHaveBeenCalledTimes(1); }); test('数字付きリストがデフォルトでは変換されないこと', async () => { const markdown = '1. First\n2. Second'; const md2sbOutput = ' 1. First\n 2. Second'; mockMd2sb.mockResolvedValue(md2sbOutput); const result = await convertMarkdownToScrapbox(markdown); expect(result).toBe(md2sbOutput); expect(mockMd2sb).toHaveBeenCalledWith(markdown); }); test('数字付きリストを明示的に箇条書きに変換できること', async () => { const markdown = '1. First\n2. Second'; const md2sbOutput = ' 1. First\n 2. Second'; const expectedScrapbox = ' First\n Second'; mockMd2sb.mockResolvedValue(md2sbOutput); const result = await convertMarkdownToScrapbox(markdown, { convertNumberedLists: true }); expect(result).toBe(expectedScrapbox); expect(mockMd2sb).toHaveBeenCalledWith(markdown); }); test('ネストした数字付きリストが箇条書きに変換されること', async () => { const markdown = '1. Parent\n 1. Child'; const md2sbOutput = ' 1. Parent\n 1. Child'; const expectedScrapbox = ' Parent\n Child'; mockMd2sb.mockResolvedValue(md2sbOutput); const result = await convertMarkdownToScrapbox(markdown, { convertNumberedLists: true }); expect(result).toBe(expectedScrapbox); }); test('空文字列が変換されること', async () => { const markdown = ''; const expectedScrapbox = ''; mockMd2sb.mockResolvedValue(expectedScrapbox); const result = await convertMarkdownToScrapbox(markdown); expect(result).toBe(expectedScrapbox); expect(mockMd2sb).toHaveBeenCalledWith(markdown); }); test('複雑なMarkdownが変換されること', async () => { const markdown = `# Main Title ## Subtitle - List item 1 - List item 2 [Link](https://example.com) \`\`\`javascript console.log('code'); \`\`\` **Bold text** and *italic text* `; const expectedScrapbox = `[* Main Title] [** Subtitle] List item 1 List item 2 [https://example.com Link] \`console.log('code');\` [** Bold text] and [/ italic text] `; mockMd2sb.mockResolvedValue(expectedScrapbox); const result = await convertMarkdownToScrapbox(markdown); expect(result).toBe(expectedScrapbox); expect(mockMd2sb).toHaveBeenCalledWith(markdown); }); test('特殊文字を含むMarkdownが変換されること', async () => { const markdown = '# Title with & < > " characters'; const expectedScrapbox = '[* Title with & < > " characters]'; mockMd2sb.mockResolvedValue(expectedScrapbox); const result = await convertMarkdownToScrapbox(markdown); expect(result).toBe(expectedScrapbox); expect(mockMd2sb).toHaveBeenCalledWith(markdown); }); test('マルチバイト文字を含むMarkdownが変換されること', async () => { const markdown = `# 日本語タイトル これは日本語のコンテンツです。 - リスト項目1 - リスト項目2 [リンク](https://example.com) `; const expectedScrapbox = `[* 日本語タイトル] これは日本語のコンテンツです。 リスト項目1 リスト項目2 [https://example.com リンク] `; mockMd2sb.mockResolvedValue(expectedScrapbox); const result = await convertMarkdownToScrapbox(markdown); expect(result).toBe(expectedScrapbox); expect(mockMd2sb).toHaveBeenCalledWith(markdown); }); test('改行を含むMarkdownが変換されること', async () => { const markdown = 'Line 1\nLine 2\n\nLine 4'; const expectedScrapbox = 'Line 1\nLine 2\n\nLine 4'; mockMd2sb.mockResolvedValue(expectedScrapbox); const result = await convertMarkdownToScrapbox(markdown); expect(result).toBe(expectedScrapbox); expect(mockMd2sb).toHaveBeenCalledWith(markdown); }); }); describe('エラーケース', () => { test('md2sbがエラーを投げた場合にエラーが伝播されること', async () => { const markdown = '# Header'; const errorMessage = 'Conversion failed'; mockMd2sb.mockRejectedValue(new Error(errorMessage)); await expect(convertMarkdownToScrapbox(markdown)).rejects.toThrow(errorMessage); expect(mockMd2sb).toHaveBeenCalledWith(markdown); }); test('md2sbが文字列エラーを投げた場合にエラーが伝播されること', async () => { const markdown = '# Header'; const errorMessage = 'String error'; mockMd2sb.mockRejectedValue(errorMessage); await expect(convertMarkdownToScrapbox(markdown)).rejects.toBe(errorMessage); expect(mockMd2sb).toHaveBeenCalledWith(markdown); }); test('md2sbがnullを返した場合の処理', async () => { const markdown = '# Header'; mockMd2sb.mockResolvedValue(null); const result = await convertMarkdownToScrapbox(markdown); expect(result).toBe(''); expect(mockMd2sb).toHaveBeenCalledWith(markdown); }); test('md2sbがundefinedを返した場合の処理', async () => { const markdown = '# Header'; mockMd2sb.mockResolvedValue(undefined); const result = await convertMarkdownToScrapbox(markdown); expect(result).toBe(''); expect(mockMd2sb).toHaveBeenCalledWith(markdown); }); }); describe('非同期処理', () => { test('非同期的に変換が実行されること', async () => { const markdown = '# Header'; const expectedScrapbox = '[* Header]'; // 遅延を伴う非同期処理をシミュレート mockMd2sb.mockImplementation(() => new Promise(resolve => setTimeout(() => resolve(expectedScrapbox), 10)) ); const startTime = Date.now(); const result = await convertMarkdownToScrapbox(markdown); const endTime = Date.now(); expect(result).toBe(expectedScrapbox); expect(endTime - startTime).toBeGreaterThanOrEqual(5); expect(mockMd2sb).toHaveBeenCalledWith(markdown); }); test('複数の変換要求が正しく処理されること', async () => { const markdowns = ['# Header 1', '# Header 2', '# Header 3']; const expectedResults = ['[* Header 1]', '[* Header 2]', '[* Header 3]']; markdowns.forEach((md, index) => { mockMd2sb.mockImplementationOnce(() => Promise.resolve(expectedResults[index]) ); }); const promises = markdowns.map(md => convertMarkdownToScrapbox(md)); const results = await Promise.all(promises); expect(results).toEqual(expectedResults); expect(mockMd2sb).toHaveBeenCalledTimes(3); markdowns.forEach((md, index) => { expect(mockMd2sb).toHaveBeenNthCalledWith(index + 1, md); }); }); }); describe('エッジケース', () => { test('非常に長いMarkdownが変換されること', async () => { const longMarkdown = 'a'.repeat(10000); const expectedScrapbox = 'converted_' + longMarkdown; mockMd2sb.mockResolvedValue(expectedScrapbox); const result = await convertMarkdownToScrapbox(longMarkdown); expect(result).toBe(expectedScrapbox); expect(mockMd2sb).toHaveBeenCalledWith(longMarkdown); }); test('制御文字を含むMarkdownが変換されること', async () => { const markdown = 'Text\twith\ttabs\nand\nnewlines\rand\rcarriage\freturns'; const expectedScrapbox = 'Converted text with special chars'; mockMd2sb.mockResolvedValue(expectedScrapbox); const result = await convertMarkdownToScrapbox(markdown); expect(result).toBe(expectedScrapbox); expect(mockMd2sb).toHaveBeenCalledWith(markdown); }); test('Unicode文字を含むMarkdownが変換されること', async () => { const markdown = '# 🎉 Emoji Title\n\n✅ Unicode bullet point\n\n🔗 [Link with emoji](https://example.com)'; const expectedScrapbox = '[* 🎉 Emoji Title]\n\n✅ Unicode bullet point\n\n[https://example.com 🔗 Link with emoji]'; mockMd2sb.mockResolvedValue(expectedScrapbox); const result = await convertMarkdownToScrapbox(markdown); expect(result).toBe(expectedScrapbox); expect(mockMd2sb).toHaveBeenCalledWith(markdown); }); }); describe('型安全性', () => { test('戻り値の型がstringであること', async () => { const markdown = '# Header'; const expectedScrapbox = '[* Header]'; mockMd2sb.mockResolvedValue(expectedScrapbox); const result = await convertMarkdownToScrapbox(markdown); expect(typeof result).toBe('string'); expect(result).toBe(expectedScrapbox); }); test('パラメータの型がstringであることを確認', async () => { const markdown = '# Header'; mockMd2sb.mockResolvedValue('[* Header]'); // TypeScriptコンパイラレベルでの型チェックを確認 await convertMarkdownToScrapbox(markdown); expect(mockMd2sb).toHaveBeenCalledWith(markdown); expect(typeof mockMd2sb.mock.calls[0][0]).toBe('string'); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/worldnine/scrapbox-cosense-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server