import assert from 'assert'; import * as mfm from '../built/index'; import { TEXT, CENTER, FN, UNI_EMOJI, MENTION, EMOJI_CODE, HASHTAG, N_URL, BOLD, SMALL, ITALIC, STRIKE, QUOTE, MATH_BLOCK, SEARCH, CODE_BLOCK, LINK } from '../built/index'; describe('PlainParser', () => { describe('text', () => { it('basic', () => { const input = 'abc'; const output = [TEXT('abc')]; assert.deepStrictEqual(mfm.parsePlain(input), output); }); it('ignore hashtag', () => { const input = 'abc#abc'; const output = [TEXT('abc#abc')]; assert.deepStrictEqual(mfm.parsePlain(input), output); }); it('keycap number sign', () => { const input = 'abc#️⃣abc'; const output = [TEXT('abc'), UNI_EMOJI('#️⃣'), TEXT('abc')]; assert.deepStrictEqual(mfm.parsePlain(input), output); }); }); }); describe('FullParser', () => { describe('text', () => { it('普通のテキストを入力すると1つのテキストノードが返される', () => { const input = 'abc'; const output = [TEXT('abc')]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('quote', () => { it('1行の引用ブロックを使用できる', () => { const input = '> abc'; const output = [ QUOTE([ TEXT('abc') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('複数行の引用ブロックを使用できる', () => { const input = ` > abc > 123 `; const output = [ QUOTE([ TEXT('abc\n123') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('引用ブロックはブロックをネストできる', () => { const input = ` >
> a >
`; const output = [ QUOTE([ CENTER([ TEXT('a') ]) ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('引用ブロックはインライン構文を含んだブロックをネストできる', () => { const input = ` >
> I'm @ai, An bot of misskey! >
`; const output = [ QUOTE([ CENTER([ TEXT('I\'m '), MENTION('ai', null, '@ai'), TEXT(', An bot of misskey!'), ]) ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('search', () => { describe('検索構文を使用できる', () => { it('Search', () => { const input = 'MFM 書き方 123 Search'; const output = [ SEARCH('MFM 書き方 123', input) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('[Search]', () => { const input = 'MFM 書き方 123 [Search]'; const output = [ SEARCH('MFM 書き方 123', input) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('search', () => { const input = 'MFM 書き方 123 search'; const output = [ SEARCH('MFM 書き方 123', input) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('[search]', () => { const input = 'MFM 書き方 123 [search]'; const output = [ SEARCH('MFM 書き方 123', input) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('検索', () => { const input = 'MFM 書き方 123 検索'; const output = [ SEARCH('MFM 書き方 123', input) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('[検索]', () => { const input = 'MFM 書き方 123 [検索]'; const output = [ SEARCH('MFM 書き方 123', input) ]; assert.deepStrictEqual(mfm.parse(input), output); }); }); it('ブロックの前後にあるテキストが正しく解釈される', () => { const input = 'abc\nhoge piyo bebeyo 検索\n123'; const output = [ TEXT('abc'), SEARCH('hoge piyo bebeyo', 'hoge piyo bebeyo 検索'), TEXT('123') ]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('code block', () => { it('コードブロックを使用できる', () => { const input = '```\nabc\n```'; const output = [CODE_BLOCK('abc', null)]; assert.deepStrictEqual(mfm.parse(input), output); }); it('コードブロックには複数行のコードを入力できる', () => { const input = '```\na\nb\nc\n```'; const output = [CODE_BLOCK('a\nb\nc', null)]; assert.deepStrictEqual(mfm.parse(input), output); }); it('コードブロックは言語を指定できる', () => { const input = '```js\nconst a = 1;\n```'; const output = [CODE_BLOCK('const a = 1;', 'js')]; assert.deepStrictEqual(mfm.parse(input), output); }); it('ブロックの前後にあるテキストが正しく解釈される', () => { const input = 'abc\n```\nconst abc = 1;\n```\n123'; const output = [ TEXT('abc'), CODE_BLOCK('const abc = 1;', null), TEXT('123') ]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('mathBlock', () => { it('1行の数式ブロックを使用できる', () => { const input = '\\[math1\\]'; const output = [ MATH_BLOCK('math1') ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('ブロックの前後にあるテキストが正しく解釈される', () => { const input = 'abc\n\\[math1\\]\n123'; const output = [ TEXT('abc'), MATH_BLOCK('math1'), TEXT('123') ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('行末以外に閉じタグがある場合はマッチしない', () => { const input = '\\[aaa\\]after'; const output = [ TEXT('\\[aaa\\]after') ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('行頭以外に開始タグがある場合はマッチしない', () => { const input = 'before\\[aaa\\]'; const output = [ TEXT('before\\[aaa\\]') ]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('center', () => { it('single text', () => { const input = '
abc
'; const output = [ CENTER([ TEXT('abc') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('multiple text', () => { const input = 'before\n
\nabc\n123\n\npiyo\n
\nafter'; const output = [ TEXT('before'), CENTER([ TEXT('abc\n123\n\npiyo') ]), TEXT('after') ]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('emoji code', () => { it('basic', () => { const input = ':abc:'; const output = [EMOJI_CODE('abc')]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('unicode emoji', () => { it('basic', () => { const input = '今起きた😇'; const output = [TEXT('今起きた'), UNI_EMOJI('😇')]; assert.deepStrictEqual(mfm.parse(input), output); }); it('keycap number sign', () => { const input = 'abc#️⃣123'; const output = [TEXT('abc'), UNI_EMOJI('#️⃣'), TEXT('123')]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('big', () => { it('basic', () => { const input = '***abc***'; const output = [ FN('tada', { }, [ TEXT('abc') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('内容にはインライン構文を利用できる', () => { const input = '***123**abc**123***'; const output = [ FN('tada', { }, [ TEXT('123'), BOLD([ TEXT('abc') ]), TEXT('123') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('内容は改行できる', () => { const input = '***123\n**abc**\n123***'; const output = [ FN('tada', { }, [ TEXT('123\n'), BOLD([ TEXT('abc') ]), TEXT('\n123') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('bold', () => { it('basic', () => { const input = '**abc**'; const output = [ BOLD([ TEXT('abc') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('内容にはインライン構文を利用できる', () => { const input = '**123~~abc~~123**'; const output = [ BOLD([ TEXT('123'), STRIKE([ TEXT('abc') ]), TEXT('123') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('内容は改行できる', () => { const input = '**123\n~~abc~~\n123**'; const output = [ BOLD([ TEXT('123\n'), STRIKE([ TEXT('abc') ]), TEXT('\n123') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('small', () => { it('basic', () => { const input = 'abc'; const output = [ SMALL([ TEXT('abc') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('内容にはインライン構文を利用できる', () => { const input = 'abc**123**abc'; const output = [ SMALL([ TEXT('abc'), BOLD([ TEXT('123') ]), TEXT('abc') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('内容は改行できる', () => { const input = 'abc\n**123**\nabc'; const output = [ SMALL([ TEXT('abc\n'), BOLD([ TEXT('123') ]), TEXT('\nabc') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('italic tag', () => { it('basic', () => { const input = 'abc'; const output = [ ITALIC([ TEXT('abc') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('内容にはインライン構文を利用できる', () => { const input = 'abc**123**abc'; const output = [ ITALIC([ TEXT('abc'), BOLD([ TEXT('123') ]), TEXT('abc') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('内容は改行できる', () => { const input = 'abc\n**123**\nabc'; const output = [ ITALIC([ TEXT('abc\n'), BOLD([ TEXT('123') ]), TEXT('\nabc') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('italic alt 1', () => { it('basic', () => { const input = '*abc*'; const output = [ ITALIC([ TEXT('abc') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('basic 2', () => { const input = 'before *abc* after'; const output = [ TEXT('before '), ITALIC([ TEXT('abc') ]), TEXT(' after') ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('ignore a italic syntax if the before char is neither a space nor an LF', () => { const input = 'before*abc*after'; const output = [TEXT('before*abc*after')]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('italic alt 2', () => { it('basic', () => { const input = '_abc_'; const output = [ ITALIC([ TEXT('abc') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('basic 2', () => { const input = 'before _abc_ after'; const output = [ TEXT('before '), ITALIC([ TEXT('abc') ]), TEXT(' after') ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('ignore a italic syntax if the before char is neither a space nor an LF', () => { const input = 'before_abc_after'; const output = [TEXT('before_abc_after')]; assert.deepStrictEqual(mfm.parse(input), output); }); }); // strike // inlineCode // mathInline describe('mention', () => { it('basic', () => { const input = '@abc'; const output = [MENTION('abc', null, '@abc')]; assert.deepStrictEqual(mfm.parse(input), output); }); it('basic 2', () => { const input = 'before @abc after'; const output = [TEXT('before '), MENTION('abc', null, '@abc'), TEXT(' after')]; assert.deepStrictEqual(mfm.parse(input), output); }); it('basic remote', () => { const input = '@abc@misskey.io'; const output = [MENTION('abc', 'misskey.io', '@abc@misskey.io')]; assert.deepStrictEqual(mfm.parse(input), output); }); it('basic remote 2', () => { const input = 'before @abc@misskey.io after'; const output = [TEXT('before '), MENTION('abc', 'misskey.io', '@abc@misskey.io'), TEXT(' after')]; assert.deepStrictEqual(mfm.parse(input), output); }); it('basic remote 3', () => { const input = 'before\n@abc@misskey.io\nafter'; const output = [TEXT('before\n'), MENTION('abc', 'misskey.io', '@abc@misskey.io'), TEXT('\nafter')]; assert.deepStrictEqual(mfm.parse(input), output); }); it('ignore format of mail address', () => { const input = 'abc@example.com'; const output = [TEXT('abc@example.com')]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('hashtag', () => { it('basic', () => { const input = '#abc'; const output = [HASHTAG('abc')]; assert.deepStrictEqual(mfm.parse(input), output); }); it('basic 2', () => { const input = 'before #abc after'; const output = [TEXT('before '), HASHTAG('abc'), TEXT(' after')]; assert.deepStrictEqual(mfm.parse(input), output); }); it('with keycap number sign', () => { const input = '#️⃣abc123 #abc'; const output = [UNI_EMOJI('#️⃣'), TEXT('abc123 '), HASHTAG('abc')]; assert.deepStrictEqual(mfm.parse(input), output); }); it('with keycap number sign 2', () => { const input = `abc #️⃣abc`; const output = [TEXT('abc\n'), UNI_EMOJI('#️⃣'), TEXT('abc')]; assert.deepStrictEqual(mfm.parse(input), output); }); it('ignore a hashtag if the before char is neither a space nor an LF', () => { const input = 'abc#abc'; const output = [TEXT('abc#abc')]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('url', () => { it('basic', () => { const input = 'official instance: https://misskey.io/@ai.'; const output = [ TEXT('official instance: '), N_URL('https://misskey.io/@ai'), TEXT('.') ]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('link', () => { it('basic', () => { const input = '[official instance](https://misskey.io/@ai).'; const output = [ LINK(false, 'https://misskey.io/@ai', [ TEXT('official instance') ]), TEXT('.') ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('silent flag', () => { const input = '?[official instance](https://misskey.io/@ai).'; const output = [ LINK(true, 'https://misskey.io/@ai', [ TEXT('official instance') ]), TEXT('.') ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('do not yield url node even if label is recognisable as a url', () => { const input = 'official instance: [https://misskey.io/@ai](https://misskey.io/@ai).'; const output = [ TEXT('official instance: '), LINK(false, 'https://misskey.io/@ai', [ TEXT('https://misskey.io/@ai') ]), TEXT('.') ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('do not yield link node even if label is recognisable as a link', () => { const input = 'official instance: [[https://misskey.io/@ai](https://misskey.io/@ai)](https://misskey.io/@ai).'; const output = [ TEXT('official instance: '), LINK(false, 'https://misskey.io/@ai', [ TEXT('[https://misskey.io/@ai](https://misskey.io/@ai)') ]), TEXT('.') ]; assert.deepStrictEqual(mfm.parse(input), output); }); }); describe('fn', () => { it('basic', () => { const input = '[tada abc]'; const output = [ FN('tada', { }, [ TEXT('abc') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); it('with a string argument', () => { const input = '[spin.speed=1.1s a]'; const output = [ FN('spin', { speed: '1.1s' }, [ TEXT('a') ]) ]; assert.deepStrictEqual(mfm.parse(input), output); }); }); it('composite', () => { const input = `before
Hello [tada everynyan! 🎉] I'm @ai, A bot of misskey! https://github.com/syuilo/ai
after`; const output = [ TEXT('before'), CENTER([ TEXT('Hello '), FN('tada', { }, [ TEXT('everynyan! '), UNI_EMOJI('🎉') ]), TEXT('\n\nI\'m '), MENTION('ai', null, '@ai'), TEXT(', A bot of misskey!\n\n'), N_URL('https://github.com/syuilo/ai') ]), TEXT('after') ]; assert.deepStrictEqual(mfm.parse(input), output); }); });