mirror of
https://github.com/tenrok/BBob.git
synced 2026-06-08 17:22:26 +03:00
fix(289): contextFreeTags closing tag bug (#290)
* feat: add tests * fix: parsing context free * refactor: code style * chore: add changeset * fix: disable coveralls
This commit is contained in:
@@ -1,12 +1,36 @@
|
||||
import { parse } from '../src';
|
||||
import type { TagNode, TagNodeTree } from "@bbob/types";
|
||||
|
||||
describe('Parser', () => {
|
||||
const expectOutput = (ast: TagNodeTree, output: Partial<TagNodeTree>) => {
|
||||
expect(ast).toBeInstanceOf(Array);
|
||||
expect(ast).toMatchObject(output as {} | TagNode[]);
|
||||
};
|
||||
const astToJSON = (ast: TagNodeTree) => Array.isArray(ast) ? ast.map(item => {
|
||||
if (typeof item === 'object' && typeof item.toJSON === 'function') {
|
||||
return item.toJSON()
|
||||
}
|
||||
|
||||
return item
|
||||
}) : ast
|
||||
|
||||
declare global {
|
||||
namespace jest {
|
||||
interface Matchers<R> {
|
||||
toBeMatchAST(expected: Array<unknown>): CustomMatcherResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect.extend({
|
||||
toBeMatchAST(ast, output) {
|
||||
|
||||
expect(astToJSON(ast)).toMatchObject(output as {} | TagNode[]);
|
||||
|
||||
return {
|
||||
message: () =>
|
||||
`no valid output`,
|
||||
pass: true,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
describe('Parser', () => {
|
||||
test('parse paired tags tokens', () => {
|
||||
const ast = parse('[best name=value]Foo Bar[/best]');
|
||||
const output = [
|
||||
@@ -31,7 +55,7 @@ describe('Parser', () => {
|
||||
},
|
||||
];
|
||||
|
||||
expectOutput(ast, output);
|
||||
expect(ast).toBeMatchAST(output);
|
||||
});
|
||||
|
||||
test('parse paired tags tokens 2', () => {
|
||||
@@ -56,7 +80,7 @@ describe('Parser', () => {
|
||||
},
|
||||
];
|
||||
|
||||
expectOutput(ast, output);
|
||||
expect(ast).toBeMatchAST(output);
|
||||
});
|
||||
|
||||
describe('onlyAllowTags', () => {
|
||||
@@ -87,7 +111,7 @@ describe('Parser', () => {
|
||||
},
|
||||
];
|
||||
|
||||
expectOutput(ast, output);
|
||||
expect(ast).toBeMatchAST(output);
|
||||
});
|
||||
|
||||
test('parse only allowed tags with params', () => {
|
||||
@@ -96,7 +120,7 @@ describe('Parser', () => {
|
||||
};
|
||||
const ast = parse('hello [blah foo="bar"]world[/blah]', options);
|
||||
|
||||
expectOutput(ast, [
|
||||
expect(ast).toBeMatchAST([
|
||||
'hello',
|
||||
' ',
|
||||
'[blah foo="bar"]',
|
||||
@@ -111,7 +135,7 @@ describe('Parser', () => {
|
||||
};
|
||||
const ast = parse('hello [blah="bar"]world[/blah]', options);
|
||||
|
||||
expectOutput(ast, [
|
||||
expect(ast).toBeMatchAST([
|
||||
'hello',
|
||||
' ',
|
||||
'[blah="bar"]',
|
||||
@@ -180,7 +204,7 @@ describe('Parser', () => {
|
||||
'[/tab]',
|
||||
];
|
||||
|
||||
expectOutput(ast, output);
|
||||
expect(ast).toBeMatchAST(output);
|
||||
});
|
||||
|
||||
test('parse only allowed tags case insensitive', () => {
|
||||
@@ -210,7 +234,7 @@ describe('Parser', () => {
|
||||
},
|
||||
];
|
||||
|
||||
expectOutput(ast, output);
|
||||
expect(ast).toBeMatchAST(output);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -243,8 +267,53 @@ describe('Parser', () => {
|
||||
}
|
||||
];
|
||||
|
||||
expectOutput(ast, output);
|
||||
expect(ast).toBeMatchAST(output);
|
||||
});
|
||||
|
||||
test('nesting similar context free tags [code][codeButton]text[/codeButton][/code]', () => {
|
||||
const ast = parse('[code][codeButton]text[/codeButton][/code]', {
|
||||
contextFreeTags: ['code']
|
||||
});
|
||||
const output = [
|
||||
{
|
||||
tag: 'code',
|
||||
attrs: {},
|
||||
content: [
|
||||
'[',
|
||||
'codeButton]text',
|
||||
'[',
|
||||
'/codeButton]'
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
expect(ast).toBeMatchAST(output);
|
||||
})
|
||||
|
||||
test('broken nesting similar context free tags [code][codeButton]text[/codeButton][code]', () => {
|
||||
const ast = parse('[code][codeButton]text[/codeButton][code]', {
|
||||
contextFreeTags: ['code']
|
||||
});
|
||||
const output = [
|
||||
{
|
||||
attrs: {},
|
||||
content: [],
|
||||
tag: 'code',
|
||||
},
|
||||
{
|
||||
attrs: {},
|
||||
content: ['text'],
|
||||
tag: 'codeButton',
|
||||
},
|
||||
{
|
||||
attrs: {},
|
||||
content: [],
|
||||
tag: 'code',
|
||||
},
|
||||
];
|
||||
|
||||
expect(ast).toBeMatchAST(output);
|
||||
})
|
||||
});
|
||||
|
||||
describe('caseFreeTags', () => {
|
||||
@@ -268,7 +337,7 @@ describe('Parser', () => {
|
||||
"[/H1]"
|
||||
];
|
||||
|
||||
expectOutput(ast, output);
|
||||
expect(ast).toBeMatchAST(output);
|
||||
});
|
||||
|
||||
test('case free tags', () => {
|
||||
@@ -295,10 +364,57 @@ describe('Parser', () => {
|
||||
}
|
||||
];
|
||||
|
||||
expectOutput(ast, output);
|
||||
expect(ast).toBeMatchAST(output);
|
||||
});
|
||||
})
|
||||
|
||||
test('nesting similar tags [code][codeButton]text[/codeButton][/code]', () => {
|
||||
const ast = parse('[code][codeButton]text[/codeButton][/code]');
|
||||
const output = [
|
||||
{
|
||||
tag: 'code',
|
||||
attrs: {},
|
||||
content: [
|
||||
{
|
||||
tag: 'codeButton',
|
||||
attrs: {},
|
||||
content: [
|
||||
'text'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
expect(ast).toBeMatchAST(output);
|
||||
})
|
||||
|
||||
test('forgot close code tag [code][codeButton]text[/codeButton][code]', () => {
|
||||
const ast = parse('[code][codeButton]text[/codeButton][code]');
|
||||
const output = [
|
||||
{
|
||||
tag: 'code',
|
||||
attrs: {},
|
||||
content: []
|
||||
},
|
||||
{
|
||||
tag: 'codeButton',
|
||||
attrs: {},
|
||||
content: [
|
||||
'text'
|
||||
]
|
||||
},
|
||||
{
|
||||
tag: 'code',
|
||||
attrs: {},
|
||||
content: []
|
||||
}
|
||||
];
|
||||
|
||||
expect(ast).toBeMatchAST(output);
|
||||
})
|
||||
|
||||
|
||||
test('parse inconsistent tags', () => {
|
||||
const ast = parse('[h1 name=value]Foo [Bar] /h1]');
|
||||
const output = [
|
||||
@@ -316,7 +432,7 @@ describe('Parser', () => {
|
||||
'Foo',
|
||||
' ',
|
||||
{
|
||||
tag: 'bar',
|
||||
tag: 'Bar',
|
||||
attrs: {},
|
||||
content: [],
|
||||
start: {
|
||||
@@ -328,7 +444,7 @@ describe('Parser', () => {
|
||||
'/h1]',
|
||||
];
|
||||
|
||||
expectOutput(ast, output);
|
||||
expect(ast).toBeMatchAST(output);
|
||||
});
|
||||
|
||||
test('parse closed tag', () => {
|
||||
@@ -337,7 +453,7 @@ describe('Parser', () => {
|
||||
'[/h1]',
|
||||
];
|
||||
|
||||
expectOutput(ast, output);
|
||||
expect(ast).toBeMatchAST(output);
|
||||
});
|
||||
|
||||
test('parse tag with value param', () => {
|
||||
@@ -360,7 +476,7 @@ describe('Parser', () => {
|
||||
},
|
||||
];
|
||||
|
||||
expectOutput(ast, output);
|
||||
expect(ast).toBeMatchAST(output);
|
||||
});
|
||||
|
||||
test('parse tag with quoted param with spaces', () => {
|
||||
@@ -385,7 +501,7 @@ describe('Parser', () => {
|
||||
},
|
||||
];
|
||||
|
||||
expectOutput(ast, output);
|
||||
expect(ast).toBeMatchAST(output);
|
||||
});
|
||||
|
||||
test('parse single tag with params', () => {
|
||||
@@ -404,7 +520,7 @@ describe('Parser', () => {
|
||||
},
|
||||
];
|
||||
|
||||
expectOutput(ast, output);
|
||||
expect(ast).toBeMatchAST(output);
|
||||
});
|
||||
|
||||
test('detect inconsistent tag', () => {
|
||||
@@ -463,14 +579,14 @@ describe('Parser', () => {
|
||||
},
|
||||
];
|
||||
|
||||
expectOutput(ast, output);
|
||||
expect(ast).toBeMatchAST(output);
|
||||
});
|
||||
|
||||
// @TODO: this is breaking change behavior
|
||||
test.skip('parse tags with single attributes like disabled', () => {
|
||||
const ast = parse('[b]hello[/b] [textarea disabled]world[/textarea]');
|
||||
|
||||
expectOutput(ast, [
|
||||
expect(ast).toBeMatchAST([
|
||||
{
|
||||
tag: 'b',
|
||||
attrs: {},
|
||||
@@ -506,7 +622,7 @@ describe('Parser', () => {
|
||||
test('parse url tag with get params', () => {
|
||||
const ast = parse('[url=https://github.com/JiLiZART/bbob/search?q=any&unscoped_q=any]GET[/url]');
|
||||
|
||||
expectOutput(ast, [
|
||||
expect(ast).toBeMatchAST([
|
||||
{
|
||||
tag: 'url',
|
||||
attrs: {
|
||||
@@ -531,7 +647,7 @@ describe('Parser', () => {
|
||||
attr value"] this is a spoiler
|
||||
[b]this is bold [i]this is bold and italic[/i] this is bold again[/b]
|
||||
[/spoiler]this is outside again`);
|
||||
expectOutput(ast, [
|
||||
expect(ast).toBeMatchAST([
|
||||
"this",
|
||||
" ",
|
||||
"is",
|
||||
@@ -632,7 +748,7 @@ describe('Parser', () => {
|
||||
[avatar href="/avatar/4/3/b/1606.jpg@20x20?cache=1561462725&bgclr=ffffff" size=xs][/avatar]
|
||||
Group Name Go[/url] `);
|
||||
|
||||
expectOutput(ast, [
|
||||
expect(ast).toBeMatchAST([
|
||||
{
|
||||
tag: 'url',
|
||||
attrs: {
|
||||
@@ -684,7 +800,7 @@ describe('Parser', () => {
|
||||
test('parse url tag with # and = symbols [google docs]', () => {
|
||||
const ast = parse('[url href=https://docs.google.com/spreadsheets/d/1W9VPUESF_NkbSa_HtRFrQNl0nYo8vPCxJFy7jD3Tpio/edit#gid=0]Docs[/url]');
|
||||
|
||||
expectOutput(ast, [
|
||||
expect(ast).toBeMatchAST([
|
||||
{
|
||||
tag: 'url',
|
||||
attrs: {
|
||||
@@ -710,8 +826,7 @@ sdfasdfasdf
|
||||
|
||||
[url=xxx]xxx[/url]`;
|
||||
|
||||
expectOutput(
|
||||
parse(str),
|
||||
expect(parse(str)).toBeMatchAST(
|
||||
[
|
||||
{
|
||||
tag: 'quote', attrs: {}, content: ['some'],
|
||||
@@ -760,8 +875,7 @@ sdfasdfasdf
|
||||
test('parse with lost closing tag on from', () => {
|
||||
const str = `[quote]xxxsdfasdf[quote]some[/quote][color=red]test[/color]sdfasdfasdf[url=xxx]xxx[/url]`;
|
||||
|
||||
expectOutput(
|
||||
parse(str),
|
||||
expect(parse(str)).toBeMatchAST(
|
||||
[
|
||||
'[quote]',
|
||||
'xxxsdfasdf',
|
||||
@@ -806,8 +920,7 @@ sdfasdfasdf
|
||||
test('parse with lost closing tag on to', () => {
|
||||
const str = `[quote]some[/quote][color=red]test[/color]sdfasdfasdf[url=xxx]xxx[/url][quote]xxxsdfasdf`;
|
||||
|
||||
expectOutput(
|
||||
parse(str),
|
||||
expect(parse(str)).toBeMatchAST(
|
||||
[
|
||||
{
|
||||
tag: 'quote', attrs: {}, content: ['some'],
|
||||
@@ -852,7 +965,7 @@ sdfasdfasdf
|
||||
test('parse with url in tag content', () => {
|
||||
const input = parse('[img]https://tw.greywool.com/i/e3Ph5.png[/img]');
|
||||
|
||||
expectOutput(input, [
|
||||
expect(input).toBeMatchAST([
|
||||
{
|
||||
tag: 'img',
|
||||
attrs: {},
|
||||
@@ -874,7 +987,7 @@ sdfasdfasdf
|
||||
whitespaceInTags: false
|
||||
})
|
||||
|
||||
expectOutput(input, [
|
||||
expect(input).toBeMatchAST([
|
||||
{
|
||||
tag: 'b',
|
||||
attrs: {},
|
||||
@@ -913,7 +1026,7 @@ sdfasdfasdf
|
||||
const content = `<button id="test0" class="value0" title="value1">class="value0" title="value1"</button>`;
|
||||
const ast = parseHTML(content);
|
||||
|
||||
expectOutput(ast, [
|
||||
expect(ast).toBeMatchAST([
|
||||
{
|
||||
"tag": "button",
|
||||
"attrs": {
|
||||
@@ -942,7 +1055,7 @@ sdfasdfasdf
|
||||
const content = `<button id="test1" class=value2 disabled required>class=value2 disabled</button>`;
|
||||
const ast = parseHTML(content);
|
||||
|
||||
expectOutput(ast, [
|
||||
expect(ast).toBeMatchAST([
|
||||
{
|
||||
"tag": "button",
|
||||
"attrs": {
|
||||
@@ -972,7 +1085,7 @@ sdfasdfasdf
|
||||
const content = `<button id="test2" class="value4"title="value5">class="value4"title="value5"</button>`;
|
||||
const ast = parseHTML(content);
|
||||
|
||||
expectOutput(ast, [
|
||||
expect(ast).toBeMatchAST([
|
||||
{
|
||||
"tag": "button",
|
||||
"attrs": {
|
||||
@@ -1000,7 +1113,7 @@ sdfasdfasdf
|
||||
enableEscapeTags: true
|
||||
});
|
||||
|
||||
expectOutput(ast, [
|
||||
expect(ast).toBeMatchAST([
|
||||
'[',
|
||||
'b',
|
||||
']',
|
||||
@@ -1016,7 +1129,7 @@ sdfasdfasdf
|
||||
enableEscapeTags: true
|
||||
});
|
||||
|
||||
expectOutput(ast, [
|
||||
expect(ast).toBeMatchAST([
|
||||
'\\',
|
||||
'[',
|
||||
'b',
|
||||
|
||||
Reference in New Issue
Block a user