mirror of
https://activitypub.software/TransFem-org/sfm-js
synced 2024-11-25 07:25:13 +00:00
Merge branch 'develop' into master
This commit is contained in:
commit
1cd5b0602b
6 changed files with 670 additions and 644 deletions
58
src/api.ts
Normal file
58
src/api.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import peg from 'pegjs';
|
||||
import { MfmNode, MfmPlainNode } from './node';
|
||||
import { stringifyNode, stringifyTree } from './util';
|
||||
|
||||
const parser: peg.Parser = require('./parser');
|
||||
|
||||
export function parse(input: string): MfmNode[] {
|
||||
const nodes = parser.parse(input, { startRule: 'fullParser' });
|
||||
return nodes;
|
||||
}
|
||||
|
||||
export function parsePlain(input: string): MfmPlainNode[] {
|
||||
const nodes = parser.parse(input, { startRule: 'plainParser' });
|
||||
return nodes;
|
||||
}
|
||||
|
||||
export function toString(tree: MfmNode[]): string
|
||||
export function toString(node: MfmNode): string
|
||||
export function toString(node: MfmNode | MfmNode[]): string {
|
||||
if (Array.isArray(node)) {
|
||||
return stringifyTree(node);
|
||||
}
|
||||
else {
|
||||
return stringifyNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
export function inspect(tree: MfmNode[], action: (node: MfmNode) => void): void {
|
||||
for (const node of tree) {
|
||||
action(node);
|
||||
if (node.children != null) {
|
||||
inspect(node.children, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function extract(nodes: MfmNode[], type: (MfmNode['type'] | MfmNode['type'][])): MfmNode[] {
|
||||
function predicate(node: MfmNode, type: (MfmNode['type'] | MfmNode['type'][])): boolean {
|
||||
if (Array.isArray(type)) {
|
||||
return (type.some(i => i == node.type));
|
||||
}
|
||||
else {
|
||||
return (type == node.type);
|
||||
}
|
||||
}
|
||||
|
||||
const dest = [] as MfmNode[];
|
||||
for (const node of nodes) {
|
||||
if (predicate(node, type)) {
|
||||
dest.push(node);
|
||||
}
|
||||
if (node.children != null) {
|
||||
dest.push(...extract(node.children, type));
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
64
src/index.ts
64
src/index.ts
|
@ -1,60 +1,10 @@
|
|||
import peg from 'pegjs';
|
||||
import { MfmNode, MfmPlainNode } from './node';
|
||||
import { stringifyNode, stringifyTree } from './util';
|
||||
const parser: peg.Parser = require('./parser');
|
||||
|
||||
export function parse(input: string): MfmNode[] {
|
||||
const nodes = parser.parse(input, { startRule: 'fullParser' });
|
||||
return nodes;
|
||||
}
|
||||
|
||||
export function parsePlain(input: string): MfmPlainNode[] {
|
||||
const nodes = parser.parse(input, { startRule: 'plainParser' });
|
||||
return nodes;
|
||||
}
|
||||
|
||||
export function toString(tree: MfmNode[]): string
|
||||
export function toString(node: MfmNode): string
|
||||
export function toString(node: MfmNode | MfmNode[]): string {
|
||||
if (Array.isArray(node)) {
|
||||
return stringifyTree(node);
|
||||
}
|
||||
else {
|
||||
return stringifyNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
export function inspect(tree: MfmNode[], action: (node: MfmNode) => void): void {
|
||||
for (const node of tree) {
|
||||
action(node);
|
||||
if (node.children != null) {
|
||||
inspect(node.children, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function extract(nodes: MfmNode[], type: (MfmNode['type'] | MfmNode['type'][])): MfmNode[] {
|
||||
function predicate(node: MfmNode, type: (MfmNode['type'] | MfmNode['type'][])): boolean {
|
||||
if (Array.isArray(type)) {
|
||||
return (type.some(i => i == node.type));
|
||||
}
|
||||
else {
|
||||
return (type == node.type);
|
||||
}
|
||||
}
|
||||
|
||||
const dest = [] as MfmNode[];
|
||||
for (const node of nodes) {
|
||||
if (predicate(node, type)) {
|
||||
dest.push(node);
|
||||
}
|
||||
if (node.children != null) {
|
||||
dest.push(...extract(node.children, type));
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
export {
|
||||
parse,
|
||||
parsePlain,
|
||||
toString,
|
||||
inspect,
|
||||
extract
|
||||
} from './api';
|
||||
|
||||
export { NodeType } from './node';
|
||||
|
||||
|
|
58
test/api.ts
Normal file
58
test/api.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import assert from 'assert';
|
||||
import { extract, inspect, parse, toString } 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 './node';
|
||||
|
||||
describe('API', () => {
|
||||
describe('toString', () => {
|
||||
it('basic', () => {
|
||||
const input =
|
||||
`before
|
||||
<center>
|
||||
Hello [tada everynyan! 🎉]
|
||||
|
||||
I'm @ai, A bot of misskey!
|
||||
|
||||
https://github.com/syuilo/ai
|
||||
</center>
|
||||
after`;
|
||||
assert.strictEqual(toString(parse(input)), input);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inspect', () => {
|
||||
it('replace text', () => {
|
||||
const input = 'good morning [tada everynyan!]';
|
||||
const result = parse(input);
|
||||
inspect(result, node => {
|
||||
if (node.type == 'text') {
|
||||
node.props.text = node.props.text.replace(/good morning/g, 'hello');
|
||||
}
|
||||
});
|
||||
assert.strictEqual(toString(result), 'hello [tada everynyan!]');
|
||||
});
|
||||
});
|
||||
|
||||
describe('extract', () => {
|
||||
it('basic', () => {
|
||||
const nodes = parse('@hoge @piyo @bebeyo');
|
||||
const expect = [
|
||||
MENTION('hoge', null, '@hoge'),
|
||||
MENTION('piyo', null, '@piyo'),
|
||||
MENTION('bebeyo', null, '@bebeyo')
|
||||
];
|
||||
assert.deepStrictEqual(extract(nodes, 'mention'), expect);
|
||||
});
|
||||
|
||||
it('nested', () => {
|
||||
const nodes = parse('abc:hoge:[tada 123 @hoge :foo:]:piyo:');
|
||||
const expect = [
|
||||
EMOJI_CODE('hoge'),
|
||||
EMOJI_CODE('foo'),
|
||||
EMOJI_CODE('piyo')
|
||||
];
|
||||
assert.deepStrictEqual(extract(nodes, 'emojiCode'), expect);
|
||||
});
|
||||
});
|
||||
});
|
564
test/main.ts
564
test/main.ts
|
@ -1,564 +0,0 @@
|
|||
import assert from 'assert';
|
||||
import { extract, inspect, parse, parsePlain, toString } from '../built/index';
|
||||
import { createNode } from '../built/util';
|
||||
import {
|
||||
TEXT, CENTER, FN, UNI_EMOJI, MENTION, EMOJI_CODE, HASHTAG, N_URL, BOLD, SMALL, ITALIC, STRIKE, QUOTE, MATH_BLOCK, SEARCH, CODE_BLOCK, LINK
|
||||
} from './node';
|
||||
|
||||
describe('text', () => {
|
||||
it('basic', () => {
|
||||
const input = 'abc';
|
||||
const output = [TEXT('abc')];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('quote', () => {
|
||||
it('single', () => {
|
||||
const input = '> abc';
|
||||
const output = [
|
||||
QUOTE([
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('multiple', () => {
|
||||
const input = `
|
||||
> abc
|
||||
> 123
|
||||
`;
|
||||
const output = [
|
||||
QUOTE([
|
||||
TEXT('abc\n123')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
|
||||
it('with block (center)', () => {
|
||||
const input = `
|
||||
> <center>
|
||||
> a
|
||||
> </center>
|
||||
`;
|
||||
const output = [
|
||||
QUOTE([
|
||||
CENTER([
|
||||
TEXT('a')
|
||||
])
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
|
||||
it('with block (center, mention)', () => {
|
||||
const input = `
|
||||
> <center>
|
||||
> I'm @ai, An bot of misskey!
|
||||
> </center>
|
||||
`;
|
||||
const output = [
|
||||
QUOTE([
|
||||
CENTER([
|
||||
TEXT('I\'m '),
|
||||
MENTION('ai', null, '@ai'),
|
||||
TEXT(', An bot of misskey!'),
|
||||
])
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
describe('basic', () => {
|
||||
it('Search', () => {
|
||||
const input = 'MFM 書き方 123 Search';
|
||||
const output = [
|
||||
createNode('search', {
|
||||
query: 'MFM 書き方 123',
|
||||
content: input
|
||||
})
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('[Search]', () => {
|
||||
const input = 'MFM 書き方 123 [Search]';
|
||||
const output = [
|
||||
createNode('search', {
|
||||
query: 'MFM 書き方 123',
|
||||
content: input
|
||||
})
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('search', () => {
|
||||
const input = 'MFM 書き方 123 search';
|
||||
const output = [
|
||||
createNode('search', {
|
||||
query: 'MFM 書き方 123',
|
||||
content: input
|
||||
})
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('[search]', () => {
|
||||
const input = 'MFM 書き方 123 [search]';
|
||||
const output = [
|
||||
createNode('search', {
|
||||
query: 'MFM 書き方 123',
|
||||
content: input
|
||||
})
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('検索', () => {
|
||||
const input = 'MFM 書き方 123 検索';
|
||||
const output = [
|
||||
createNode('search', {
|
||||
query: 'MFM 書き方 123',
|
||||
content: input
|
||||
})
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('[検索]', () => {
|
||||
const input = 'MFM 書き方 123 [検索]';
|
||||
const output = [
|
||||
createNode('search', {
|
||||
query: 'MFM 書き方 123',
|
||||
content: input
|
||||
})
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
it('with text', () => {
|
||||
const input = 'abc\nhoge piyo bebeyo 検索\n123';
|
||||
const output = [
|
||||
TEXT('abc'),
|
||||
SEARCH('hoge piyo bebeyo', 'hoge piyo bebeyo 検索'),
|
||||
TEXT('123')
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('code block', () => {
|
||||
it('basic', () => {
|
||||
const input = '```\nabc\n```';
|
||||
const output = [CODE_BLOCK('abc', null)];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('multi line', () => {
|
||||
const input = '```\na\nb\nc\n```';
|
||||
const output = [CODE_BLOCK('a\nb\nc', null)];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('basic (lang)', () => {
|
||||
const input = '```js\nconst a = 1;\n```';
|
||||
const output = [CODE_BLOCK('const a = 1;', 'js')];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('with text', () => {
|
||||
const input = 'abc\n```\nconst abc = 1;\n```\n123';
|
||||
const output = [
|
||||
TEXT('abc'),
|
||||
CODE_BLOCK('const abc = 1;', null),
|
||||
TEXT('123')
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mathBlock', () => {
|
||||
it('basic', () => {
|
||||
const input = '123\n\\[math1\\]\nabc\n\\[math2\\]';
|
||||
const output = [
|
||||
TEXT('123'),
|
||||
MATH_BLOCK('math1'),
|
||||
TEXT('abc'),
|
||||
MATH_BLOCK('math2')
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('case of no matched', () => {
|
||||
const input = '\\[aaa\\]\\[bbb\\]';
|
||||
const output = [
|
||||
TEXT('\\[aaa\\]\\[bbb\\]')
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('center', () => {
|
||||
it('single text', () => {
|
||||
const input = '<center>abc</center>';
|
||||
const output = [
|
||||
CENTER([
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('multiple text', () => {
|
||||
const input = 'before\n<center>\nabc\n123\n\npiyo\n</center>\nafter';
|
||||
const output = [
|
||||
TEXT('before'),
|
||||
CENTER([
|
||||
TEXT('abc\n123\n\npiyo')
|
||||
]),
|
||||
TEXT('after')
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('emoji code', () => {
|
||||
it('basic', () => {
|
||||
const input = ':abc:';
|
||||
const output = [EMOJI_CODE('abc')];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unicode emoji', () => {
|
||||
it('basic', () => {
|
||||
const input = '今起きた😇';
|
||||
const output = [TEXT('今起きた'), UNI_EMOJI('😇')];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('big', () => {
|
||||
it('basic', () => {
|
||||
const input = '***abc***';
|
||||
const output = [
|
||||
FN('tada', { }, [
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容にはインライン構文を利用できる', () => {
|
||||
const input = '***123**abc**123***';
|
||||
const output = [
|
||||
FN('tada', { }, [
|
||||
TEXT('123'),
|
||||
BOLD([
|
||||
TEXT('abc')
|
||||
]),
|
||||
TEXT('123')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容は改行できる', () => {
|
||||
const input = '***123\n**abc**\n123***';
|
||||
const output = [
|
||||
FN('tada', { }, [
|
||||
TEXT('123\n'),
|
||||
BOLD([
|
||||
TEXT('abc')
|
||||
]),
|
||||
TEXT('\n123')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bold', () => {
|
||||
it('basic', () => {
|
||||
const input = '**abc**';
|
||||
const output = [
|
||||
BOLD([
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容にはインライン構文を利用できる', () => {
|
||||
const input = '**123~~abc~~123**';
|
||||
const output = [
|
||||
BOLD([
|
||||
TEXT('123'),
|
||||
STRIKE([
|
||||
TEXT('abc')
|
||||
]),
|
||||
TEXT('123')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容は改行できる', () => {
|
||||
const input = '**123\n~~abc~~\n123**';
|
||||
const output = [
|
||||
BOLD([
|
||||
TEXT('123\n'),
|
||||
STRIKE([
|
||||
TEXT('abc')
|
||||
]),
|
||||
TEXT('\n123')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('small', () => {
|
||||
it('basic', () => {
|
||||
const input = '<small>abc</small>';
|
||||
const output = [
|
||||
SMALL([
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容にはインライン構文を利用できる', () => {
|
||||
const input = '<small>abc**123**abc</small>';
|
||||
const output = [
|
||||
SMALL([
|
||||
TEXT('abc'),
|
||||
BOLD([
|
||||
TEXT('123')
|
||||
]),
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容は改行できる', () => {
|
||||
const input = '<small>abc\n**123**\nabc</small>';
|
||||
const output = [
|
||||
SMALL([
|
||||
TEXT('abc\n'),
|
||||
BOLD([
|
||||
TEXT('123')
|
||||
]),
|
||||
TEXT('\nabc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('italic 1', () => {
|
||||
it('basic', () => {
|
||||
const input = '<i>abc</i>';
|
||||
const output = [
|
||||
ITALIC([
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容にはインライン構文を利用できる', () => {
|
||||
const input = '<i>abc**123**abc</i>';
|
||||
const output = [
|
||||
ITALIC([
|
||||
TEXT('abc'),
|
||||
BOLD([
|
||||
TEXT('123')
|
||||
]),
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容は改行できる', () => {
|
||||
const input = '<i>abc\n**123**\nabc</i>';
|
||||
const output = [
|
||||
ITALIC([
|
||||
TEXT('abc\n'),
|
||||
BOLD([
|
||||
TEXT('123')
|
||||
]),
|
||||
TEXT('\nabc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('italic 2', () => {
|
||||
it('basic', () => {
|
||||
const input = '*abc*';
|
||||
const output = [
|
||||
ITALIC([
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
// strike
|
||||
|
||||
// inlineCode
|
||||
|
||||
// mathInline
|
||||
|
||||
// mention
|
||||
|
||||
describe('hashtag', () => {
|
||||
it('and unicode emoji', () => {
|
||||
const input = '#️⃣abc123#abc';
|
||||
const output = [UNI_EMOJI('#️⃣'), TEXT('abc123'), HASHTAG('abc')];
|
||||
assert.deepStrictEqual(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(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(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(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(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(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fn', () => {
|
||||
it('basic', () => {
|
||||
const input = '[tada abc]';
|
||||
const output = [
|
||||
FN('tada', { }, [
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
it('composite', () => {
|
||||
const input =
|
||||
`before
|
||||
<center>
|
||||
Hello [tada everynyan! 🎉]
|
||||
|
||||
I'm @ai, A bot of misskey!
|
||||
|
||||
https://github.com/syuilo/ai
|
||||
</center>
|
||||
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(parse(input), output);
|
||||
});
|
||||
|
||||
describe('toString API', () => {
|
||||
it('basic', () => {
|
||||
const input =
|
||||
`before
|
||||
<center>
|
||||
Hello [tada everynyan! 🎉]
|
||||
|
||||
I'm @ai, A bot of misskey!
|
||||
|
||||
https://github.com/syuilo/ai
|
||||
</center>
|
||||
after`;
|
||||
assert.strictEqual(toString(parse(input)), input);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inspect API', () => {
|
||||
it('replace text', () => {
|
||||
const input = 'good morning [tada everynyan!]';
|
||||
const result = parse(input);
|
||||
inspect(result, node => {
|
||||
if (node.type == 'text') {
|
||||
node.props.text = node.props.text.replace(/good morning/g, 'hello');
|
||||
}
|
||||
});
|
||||
assert.strictEqual(toString(result), 'hello [tada everynyan!]');
|
||||
});
|
||||
});
|
||||
|
||||
describe('extract API', () => {
|
||||
it('basic', () => {
|
||||
const nodes = parse('@hoge @piyo @bebeyo');
|
||||
const expect = [
|
||||
MENTION('hoge', null, '@hoge'),
|
||||
MENTION('piyo', null, '@piyo'),
|
||||
MENTION('bebeyo', null, '@bebeyo')
|
||||
];
|
||||
assert.deepStrictEqual(extract(nodes, 'mention'), expect);
|
||||
});
|
||||
|
||||
it('nested', () => {
|
||||
const nodes = parse('abc:hoge:[tada 123 @hoge :foo:]:piyo:');
|
||||
const expect = [
|
||||
EMOJI_CODE('hoge'),
|
||||
EMOJI_CODE('foo'),
|
||||
EMOJI_CODE('piyo')
|
||||
];
|
||||
assert.deepStrictEqual(extract(nodes, 'emojiCode'), expect);
|
||||
});
|
||||
});
|
44
test/node.ts
44
test/node.ts
|
@ -1,26 +1,24 @@
|
|||
import {
|
||||
MfmBold, MfmCenter, MfmCodeBlock, MfmEmojiCode, MfmFn, MfmHashtag, MfmInline,
|
||||
MfmInlineCode, MfmItalic, MfmLink, MfmMathBlock, MfmMathInline, MfmMention,
|
||||
MfmNode, MfmQuote, MfmSearch, MfmSmall, MfmStrike, MfmText, MfmUnicodeEmoji, MfmUrl
|
||||
MfmFn, MfmInline, MfmNode, NodeType
|
||||
} from '../built';
|
||||
|
||||
export const QUOTE = (children: MfmNode[]): NodeType<'quote'> => { return { type:'quote', children }; };
|
||||
export const SEARCH = (query: string, content: string): NodeType<'search'> => { return { type:'search', props: { query, content } }; };
|
||||
export const CODE_BLOCK = (code: string, lang: string | null): NodeType<'blockCode'> => { return { type:'blockCode', props: { code, lang } }; };
|
||||
export const MATH_BLOCK = (formula: string): NodeType<'mathBlock'> => { return { type:'mathBlock', props: { formula } }; };
|
||||
export const CENTER = (children: MfmInline[]): NodeType<'center'> => { return { type:'center', children }; };
|
||||
|
||||
export const QUOTE = (children: MfmNode[]): MfmQuote => { return { type:'quote', children }; };
|
||||
export const SEARCH = (query: string, content: string): MfmSearch => { return { type:'search', props: { query, content } }; };
|
||||
export const CODE_BLOCK = (code: string, lang: string | null): MfmCodeBlock => { return { type:'blockCode', props: { code, lang } }; };
|
||||
export const MATH_BLOCK = (formula: string): MfmMathBlock => { return { type:'mathBlock', props: { formula } }; };
|
||||
export const CENTER = (children: MfmInline[]): MfmCenter => { return { type:'center', children }; };
|
||||
|
||||
export const BOLD = (children: MfmInline[]): MfmBold => { return { type:'bold', children }; };
|
||||
export const SMALL = (children: MfmInline[]): MfmSmall => { return { type:'small', children }; };
|
||||
export const ITALIC = (children: MfmInline[]): MfmItalic => { return { type:'italic', children }; };
|
||||
export const STRIKE = (children: MfmInline[]): MfmStrike => { return { type:'strike', children }; };
|
||||
export const INLINE_CODE = (code: string): MfmInlineCode => { return { type:'inlineCode', props: { code } }; };
|
||||
export const MATH_INLINE = (formula: string): MfmMathInline => { return { type:'mathInline', props: { formula } }; };
|
||||
export const MENTION = (username: string, host: string | null, acct: string): MfmMention => { return { type:'mention', props: { username, host, acct } }; };
|
||||
export const HASHTAG = (value: string): MfmHashtag => { return { type:'hashtag', props: { hashtag: value } }; };
|
||||
export const N_URL = (value: string): MfmUrl => { return { type:'url', props: { url: value } }; };
|
||||
export const LINK = (silent: boolean, url: string, children: MfmInline[]): MfmLink => { return { type:'link', props: { silent, url }, children }; };
|
||||
export const EMOJI_CODE = (name: string): MfmEmojiCode => { return { type:'emojiCode', props: { name: name } }; };
|
||||
export const FN = (name: string, args: MfmFn['props']['args'], children: MfmFn['children']): MfmFn => { return { type:'fn', props: { name, args }, children }; };
|
||||
export const UNI_EMOJI = (value: string): MfmUnicodeEmoji => { return { type:'unicodeEmoji', props: { emoji: value } }; };
|
||||
export const TEXT = (value: string): MfmText => { return { type:'text', props: { text: value } }; };
|
||||
export const BOLD = (children: MfmInline[]): NodeType<'bold'> => { return { type:'bold', children }; };
|
||||
export const SMALL = (children: MfmInline[]): NodeType<'small'> => { return { type:'small', children }; };
|
||||
export const ITALIC = (children: MfmInline[]): NodeType<'italic'> => { return { type:'italic', children }; };
|
||||
export const STRIKE = (children: MfmInline[]): NodeType<'strike'> => { return { type:'strike', children }; };
|
||||
export const INLINE_CODE = (code: string): NodeType<'inlineCode'> => { return { type:'inlineCode', props: { code } }; };
|
||||
export const MATH_INLINE = (formula: string): NodeType<'mathInline'> => { return { type:'mathInline', props: { formula } }; };
|
||||
export const MENTION = (username: string, host: string | null, acct: string): NodeType<'mention'> => { return { type:'mention', props: { username, host, acct } }; };
|
||||
export const HASHTAG = (value: string): NodeType<'hashtag'> => { return { type:'hashtag', props: { hashtag: value } }; };
|
||||
export const N_URL = (value: string): NodeType<'url'> => { return { type:'url', props: { url: value } }; };
|
||||
export const LINK = (silent: boolean, url: string, children: MfmInline[]): NodeType<'link'> => { return { type:'link', props: { silent, url }, children }; };
|
||||
export const EMOJI_CODE = (name: string): NodeType<'emojiCode'> => { return { type:'emojiCode', props: { name: name } }; };
|
||||
export const FN = (name: string, args: MfmFn['props']['args'], children: MfmFn['children']): NodeType<'fn'> => { return { type:'fn', props: { name, args }, children }; };
|
||||
export const UNI_EMOJI = (value: string): NodeType<'unicodeEmoji'> => { return { type:'unicodeEmoji', props: { emoji: value } }; };
|
||||
export const TEXT = (value: string): NodeType<'text'> => { return { type:'text', props: { text: value } }; };
|
||||
|
|
526
test/parser.ts
Normal file
526
test/parser.ts
Normal file
|
@ -0,0 +1,526 @@
|
|||
import assert from 'assert';
|
||||
import { parse } from '../built/index';
|
||||
import { createNode } from '../built/util';
|
||||
import {
|
||||
TEXT, CENTER, FN, UNI_EMOJI, MENTION, EMOJI_CODE, HASHTAG, N_URL, BOLD, SMALL, ITALIC, STRIKE, QUOTE, MATH_BLOCK, SEARCH, CODE_BLOCK, LINK
|
||||
} from './node';
|
||||
|
||||
describe('parser', () => {
|
||||
describe('text', () => {
|
||||
it('普通のテキストを入力すると1つのテキストノードが返される', () => {
|
||||
const input = 'abc';
|
||||
const output = [TEXT('abc')];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('quote', () => {
|
||||
it('1行の引用ブロックを使用できる', () => {
|
||||
const input = '> abc';
|
||||
const output = [
|
||||
QUOTE([
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('複数行の引用ブロックを使用できる', () => {
|
||||
const input = `
|
||||
> abc
|
||||
> 123
|
||||
`;
|
||||
const output = [
|
||||
QUOTE([
|
||||
TEXT('abc\n123')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('引用ブロックはブロックをネストできる', () => {
|
||||
const input = `
|
||||
> <center>
|
||||
> a
|
||||
> </center>
|
||||
`;
|
||||
const output = [
|
||||
QUOTE([
|
||||
CENTER([
|
||||
TEXT('a')
|
||||
])
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('引用ブロックはインライン構文を含んだブロックをネストできる', () => {
|
||||
const input = `
|
||||
> <center>
|
||||
> I'm @ai, An bot of misskey!
|
||||
> </center>
|
||||
`;
|
||||
const output = [
|
||||
QUOTE([
|
||||
CENTER([
|
||||
TEXT('I\'m '),
|
||||
MENTION('ai', null, '@ai'),
|
||||
TEXT(', An bot of misskey!'),
|
||||
])
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
describe('検索構文を使用できる', () => {
|
||||
it('Search', () => {
|
||||
const input = 'MFM 書き方 123 Search';
|
||||
const output = [
|
||||
createNode('search', {
|
||||
query: 'MFM 書き方 123',
|
||||
content: input
|
||||
})
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('[Search]', () => {
|
||||
const input = 'MFM 書き方 123 [Search]';
|
||||
const output = [
|
||||
createNode('search', {
|
||||
query: 'MFM 書き方 123',
|
||||
content: input
|
||||
})
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('search', () => {
|
||||
const input = 'MFM 書き方 123 search';
|
||||
const output = [
|
||||
createNode('search', {
|
||||
query: 'MFM 書き方 123',
|
||||
content: input
|
||||
})
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('[search]', () => {
|
||||
const input = 'MFM 書き方 123 [search]';
|
||||
const output = [
|
||||
createNode('search', {
|
||||
query: 'MFM 書き方 123',
|
||||
content: input
|
||||
})
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('検索', () => {
|
||||
const input = 'MFM 書き方 123 検索';
|
||||
const output = [
|
||||
createNode('search', {
|
||||
query: 'MFM 書き方 123',
|
||||
content: input
|
||||
})
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('[検索]', () => {
|
||||
const input = 'MFM 書き方 123 [検索]';
|
||||
const output = [
|
||||
createNode('search', {
|
||||
query: 'MFM 書き方 123',
|
||||
content: input
|
||||
})
|
||||
];
|
||||
assert.deepStrictEqual(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(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('code block', () => {
|
||||
it('コードブロックを使用できる', () => {
|
||||
const input = '```\nabc\n```';
|
||||
const output = [CODE_BLOCK('abc', null)];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('コードブロックには複数行のコードを入力できる', () => {
|
||||
const input = '```\na\nb\nc\n```';
|
||||
const output = [CODE_BLOCK('a\nb\nc', null)];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('コードブロックは言語を指定できる', () => {
|
||||
const input = '```js\nconst a = 1;\n```';
|
||||
const output = [CODE_BLOCK('const a = 1;', 'js')];
|
||||
assert.deepStrictEqual(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(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mathBlock', () => {
|
||||
it('1行の数式ブロックを使用できる', () => {
|
||||
const input = '\\[math1\\]';
|
||||
const output = [
|
||||
MATH_BLOCK('math1')
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('ブロックの前後にあるテキストが正しく解釈される', () => {
|
||||
const input = 'abc\n\\[math1\\]\n123';
|
||||
const output = [
|
||||
TEXT('abc'),
|
||||
MATH_BLOCK('math1'),
|
||||
TEXT('123')
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('行末以外に閉じタグがある場合はマッチしない', () => {
|
||||
const input = '\\[aaa\\]after';
|
||||
const output = [
|
||||
TEXT('\\[aaa\\]after')
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('行頭以外に開始タグがある場合はマッチしない', () => {
|
||||
const input = 'before\\[aaa\\]';
|
||||
const output = [
|
||||
TEXT('before\\[aaa\\]')
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('center', () => {
|
||||
it('single text', () => {
|
||||
const input = '<center>abc</center>';
|
||||
const output = [
|
||||
CENTER([
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('multiple text', () => {
|
||||
const input = 'before\n<center>\nabc\n123\n\npiyo\n</center>\nafter';
|
||||
const output = [
|
||||
TEXT('before'),
|
||||
CENTER([
|
||||
TEXT('abc\n123\n\npiyo')
|
||||
]),
|
||||
TEXT('after')
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('emoji code', () => {
|
||||
it('basic', () => {
|
||||
const input = ':abc:';
|
||||
const output = [EMOJI_CODE('abc')];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unicode emoji', () => {
|
||||
it('basic', () => {
|
||||
const input = '今起きた😇';
|
||||
const output = [TEXT('今起きた'), UNI_EMOJI('😇')];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('big', () => {
|
||||
it('basic', () => {
|
||||
const input = '***abc***';
|
||||
const output = [
|
||||
FN('tada', { }, [
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容にはインライン構文を利用できる', () => {
|
||||
const input = '***123**abc**123***';
|
||||
const output = [
|
||||
FN('tada', { }, [
|
||||
TEXT('123'),
|
||||
BOLD([
|
||||
TEXT('abc')
|
||||
]),
|
||||
TEXT('123')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容は改行できる', () => {
|
||||
const input = '***123\n**abc**\n123***';
|
||||
const output = [
|
||||
FN('tada', { }, [
|
||||
TEXT('123\n'),
|
||||
BOLD([
|
||||
TEXT('abc')
|
||||
]),
|
||||
TEXT('\n123')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bold', () => {
|
||||
it('basic', () => {
|
||||
const input = '**abc**';
|
||||
const output = [
|
||||
BOLD([
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容にはインライン構文を利用できる', () => {
|
||||
const input = '**123~~abc~~123**';
|
||||
const output = [
|
||||
BOLD([
|
||||
TEXT('123'),
|
||||
STRIKE([
|
||||
TEXT('abc')
|
||||
]),
|
||||
TEXT('123')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容は改行できる', () => {
|
||||
const input = '**123\n~~abc~~\n123**';
|
||||
const output = [
|
||||
BOLD([
|
||||
TEXT('123\n'),
|
||||
STRIKE([
|
||||
TEXT('abc')
|
||||
]),
|
||||
TEXT('\n123')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('small', () => {
|
||||
it('basic', () => {
|
||||
const input = '<small>abc</small>';
|
||||
const output = [
|
||||
SMALL([
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容にはインライン構文を利用できる', () => {
|
||||
const input = '<small>abc**123**abc</small>';
|
||||
const output = [
|
||||
SMALL([
|
||||
TEXT('abc'),
|
||||
BOLD([
|
||||
TEXT('123')
|
||||
]),
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容は改行できる', () => {
|
||||
const input = '<small>abc\n**123**\nabc</small>';
|
||||
const output = [
|
||||
SMALL([
|
||||
TEXT('abc\n'),
|
||||
BOLD([
|
||||
TEXT('123')
|
||||
]),
|
||||
TEXT('\nabc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('italic 1', () => {
|
||||
it('basic', () => {
|
||||
const input = '<i>abc</i>';
|
||||
const output = [
|
||||
ITALIC([
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容にはインライン構文を利用できる', () => {
|
||||
const input = '<i>abc**123**abc</i>';
|
||||
const output = [
|
||||
ITALIC([
|
||||
TEXT('abc'),
|
||||
BOLD([
|
||||
TEXT('123')
|
||||
]),
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
it('内容は改行できる', () => {
|
||||
const input = '<i>abc\n**123**\nabc</i>';
|
||||
const output = [
|
||||
ITALIC([
|
||||
TEXT('abc\n'),
|
||||
BOLD([
|
||||
TEXT('123')
|
||||
]),
|
||||
TEXT('\nabc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('italic 2', () => {
|
||||
it('basic', () => {
|
||||
const input = '*abc*';
|
||||
const output = [
|
||||
ITALIC([
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
// strike
|
||||
|
||||
// inlineCode
|
||||
|
||||
// mathInline
|
||||
|
||||
// mention
|
||||
|
||||
describe('hashtag', () => {
|
||||
it('and unicode emoji', () => {
|
||||
const input = '#️⃣abc123#abc';
|
||||
const output = [UNI_EMOJI('#️⃣'), TEXT('abc123'), HASHTAG('abc')];
|
||||
assert.deepStrictEqual(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(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(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(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(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(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fn', () => {
|
||||
it('basic', () => {
|
||||
const input = '[tada abc]';
|
||||
const output = [
|
||||
FN('tada', { }, [
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
it('composite', () => {
|
||||
const input =
|
||||
`before
|
||||
<center>
|
||||
Hello [tada everynyan! 🎉]
|
||||
|
||||
I'm @ai, A bot of misskey!
|
||||
|
||||
https://github.com/syuilo/ai
|
||||
</center>
|
||||
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(parse(input), output);
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue