quill-delta-to-html
Version:
Converts Quill's delta ops to HTML
488 lines (428 loc) • 18.2 kB
text/typescript
import 'mocha';
import * as assert from 'assert';
import { DeltaInsertOp } from './../src/DeltaInsertOp';
import { QuillDeltaToHtmlConverter } from "./../src/QuillDeltaToHtmlConverter";
import { callWhenXEqualY } from './_helper';
import { delta1 } from './data/delta1';
import { GroupType, ListType } from './../src/value-types';
import { encodeHtml } from './../src/funcs-html';
describe('QuillDeltaToHtmlConverter', function () {
describe('constructor()', function () {
it('should instantiate return proper html', function () {
var qdc = new QuillDeltaToHtmlConverter(delta1.ops,
{ classPrefix: 'noz' });
var html = qdc.convert();
assert.equal(html, delta1.html);
});
});
describe("convert()", function () {
var ops2 = [
{ insert: "this is text" },
{ insert: "\n" },
{ insert: "this is code" },
{ insert: "\n", attributes: { 'code-block': true } },
{ insert: "this is code TOO!" },
{ insert: "\n", attributes: { 'code-block': true } },
];
it('should render html', function () {
var qdc = new QuillDeltaToHtmlConverter(ops2);
var html = qdc.convert();
assert.equal(html.indexOf('<pre>this is code') > -1, true, html);
});
it('should render mention', function () {
let ops = [
{
insert: "mention", attributes: {
mentions: true,
mention: {
'end-point': 'http://abc.com',
slug: 'a',
class: 'abc', target: '_blank'
}
}
}
]
var qdc = new QuillDeltaToHtmlConverter(ops);
var html = qdc.convert();
assert.equal(html, ['<p><a class="abc"',
' href="http://abc.com/a" target="_blank"',
'>mention</a></p>'
].join(""));
var qdc = new QuillDeltaToHtmlConverter([{
insert: 'mention', attributes: {
mentions: true, mention: { slug: 'aa' }
}
}]);
var html = qdc.convert();
assert.equal(html, ['<p><a',
' href="about:blank">mention</a></p>'
].join(""));
});
it('should open and close list tags', function () {
var ops4 = [
{ insert: "mr\n" },
{ insert: "hello" },
{ insert: "\n", attributes: { list: 'ordered' } },
{ insert: "there" },
{ insert: "\n", attributes: { list: 'bullet' } },
{ insert: "\n", attributes: { list: 'ordered' } },
]
var qdc = new QuillDeltaToHtmlConverter(ops4);
var html = qdc.convert();
assert.equal(html.indexOf('<p>mr') > -1, true);
assert.equal(html.indexOf('</ol><ul><li>there') > -1, true);
});
it('should wrap positional styles in right tag', function () {
var ops4 = [
{ insert: "mr" },
{ insert: "\n", attributes: { align: 'center' } },
{ insert: "\n", attributes: { direction: 'rtl' } },
{ insert: "\n", attributes: { indent: 2 } }
]
var qdc = new QuillDeltaToHtmlConverter(ops4, { paragraphTag: 'div' });
var html = qdc.convert();
assert.equal(html.indexOf('<div class="ql-align') > -1, true);
assert.equal(html.indexOf('<div class="ql-direction') > -1, true);
assert.equal(html.indexOf('<div class="ql-indent') > -1, true);
var qdc = new QuillDeltaToHtmlConverter(ops4);
var html = qdc.convert();
assert.equal(html.indexOf('<p class="ql-align') > -1, true);
assert.equal(html.indexOf('<p class="ql-direction') > -1, true);
assert.equal(html.indexOf('<p class="ql-indent') > -1, true);
});
it('should render target attr correctly', () => {
let ops = [
{"attributes":{"target":"_self","link":"#"},"insert":"A"},
{"attributes":{"target":"_blank","link":"#"},"insert":"B"},
{"attributes":{"link":"#"},"insert":"C"},{"insert":"\n"}
];
let qdc = new QuillDeltaToHtmlConverter(ops, {linkTarget: ''});
let html = qdc.convert();
assert.equal(html, [
`<p><a href="unsafe:#" target="_self">A</a>`,
`<a href="unsafe:#" target="_blank">B</a>`,
`<a href="unsafe:#">C</a></p>`
].join(''));
qdc = new QuillDeltaToHtmlConverter(ops);
html = qdc.convert();
assert.equal(html, [
`<p><a href="unsafe:#" target="_self">A</a>`,
`<a href="unsafe:#" target="_blank">B</a>`,
`<a href="unsafe:#" target="_blank">C</a></p>`
].join(''));
qdc = new QuillDeltaToHtmlConverter(ops, {linkTarget: '_top'});
html = qdc.convert();
assert.equal(html, [
`<p><a href="unsafe:#" target="_self">A</a>`,
`<a href="unsafe:#" target="_blank">B</a>`,
`<a href="unsafe:#" target="_top">C</a></p>`
].join(''));
});
});
describe('custom types', () => {
it(`should return empty string if renderer not defined for
custom blot`, () => {
let ops = [
{ insert: { customstuff: 'my val' } }
];
let qdc = new QuillDeltaToHtmlConverter(ops);
assert.equal(qdc.convert(), '<p></p>');
});
it('should render custom insert types with given renderer', () => {
let ops = [
{ insert: { bolditalic: 'my text' } },
{ insert: { blah: 1 } }
];
let qdc = new QuillDeltaToHtmlConverter(ops);
qdc.renderCustomWith((op) => {
if (op.insert.type === 'bolditalic') {
return '<b><i>' + op.insert.value + '</i></b>';
}
return 'unknown';
});
let html = qdc.convert();
assert.equal(html, '<p><b><i>my text</i></b>unknown</p>');
});
it('should render custom insert types in code blocks with given renderer', () => {
let ops = [
{ insert: { colonizer: ':' }},
{ insert: "\n", attributes: { 'code-block': true } },
{ insert: "code1"},
{ insert: "\n", attributes: { 'code-block': true } },
{ insert: { colonizer: ':' }},
{ insert: "\n", attributes: { 'code-block': true } }
];
let renderer = (op: DeltaInsertOp) => {
if (op.insert.type === 'colonizer') {
return op.insert.value;
}
return '';
};
let qdc = new QuillDeltaToHtmlConverter(ops.slice(0,2));
qdc.renderCustomWith(renderer);
assert.equal(qdc.convert(), '<pre>:</pre>');
qdc = new QuillDeltaToHtmlConverter(ops);
qdc.renderCustomWith(renderer);
assert.equal(qdc.convert(), '<pre>:\ncode1\n:</pre>');
});
it('should render custom insert types in headers with given renderer', () => {
let ops = [
{ insert: { colonizer: ':' }},
{ insert: "\n", attributes: { header: 1 } },
{ insert: "hello"},
{ insert: "\n", attributes: { header: 1 } },
{ insert: { colonizer: ':' }},
{ insert: "\n", attributes: { header: 1 } }
];
let renderer = (op: DeltaInsertOp) => {
if (op.insert.type === 'colonizer') {
return op.insert.value;
}
return '';
};
let qdc = new QuillDeltaToHtmlConverter(ops.slice(0,2));
qdc.renderCustomWith(renderer);
assert.equal(qdc.convert(), '<h1>:</h1>');
qdc = new QuillDeltaToHtmlConverter(ops);
qdc.renderCustomWith(renderer);
assert.equal(qdc.convert(), '<h1>:<br/>hello<br/>:</h1>');
});
});
describe('_getListTag()', function () {
it('should return proper list tag', function () {
var op = new DeltaInsertOp("\n", { list: ListType.Ordered });
var qdc = new QuillDeltaToHtmlConverter(delta1.ops)
assert.equal(qdc._getListTag(op), 'ol');
var op = new DeltaInsertOp("\n", { list: ListType.Bullet });
assert.equal(qdc._getListTag(op), 'ul');
var op = new DeltaInsertOp("d");
assert.equal(qdc._getListTag(op), '');
});
});
describe(' prepare data before inline and block renders', function () {
var ops: any;
beforeEach(function () {
ops = [
{ insert: 'Hello' },
{ insert: ' my ', attributes: { italic: true } },
{ insert: '\n', attributes: { italic: true } },
{ insert: ' name is joey' }
].map((v: any) => new DeltaInsertOp(v.insert, v.attributes));
});
describe('renderInlines()', function () {
it('should render inlines', function () {
var qdc = new QuillDeltaToHtmlConverter([]);
var inlines = qdc._renderInlines(ops);
assert.equal(inlines, ['<p>Hello',
'<em> my </em><br/> name is joey</p>'].join(''));
qdc = new QuillDeltaToHtmlConverter([], { paragraphTag: 'div' });
var inlines = qdc._renderInlines(ops);
assert.equal(inlines,
'<div>Hello<em> my </em><br/> name is joey</div>');
qdc = new QuillDeltaToHtmlConverter([], { paragraphTag: '' });
var inlines = qdc._renderInlines(ops);
assert.equal(inlines, 'Hello<em> my </em><br/> name is joey');
});
it('should render plain new line string', function () {
var ops = [new DeltaInsertOp("\n")];
var qdc = new QuillDeltaToHtmlConverter([]);
assert.equal(qdc._renderInlines(ops), '<p><br/></p>');
});
it('should render styled new line string', function () {
var ops = [new DeltaInsertOp("\n", { font: 'arial' })];
var qdc = new QuillDeltaToHtmlConverter([]);
assert.equal(qdc._renderInlines(ops),
'<p><br/></p>');
var qdc = new QuillDeltaToHtmlConverter([], { paragraphTag: '' });
assert.equal(qdc._renderInlines(ops), '<br/>');
});
it('should render when first line is new line', function () {
var ops = [new DeltaInsertOp("\n"), new DeltaInsertOp("aa")];
var qdc = new QuillDeltaToHtmlConverter([]);
assert.equal(qdc._renderInlines(ops), '<p><br/>aa</p>');
});
it('should render when last line is new line', function () {
var ops = [new DeltaInsertOp("aa"), new DeltaInsertOp("\n")];
var qdc = new QuillDeltaToHtmlConverter([]);
assert.equal(qdc._renderInlines(ops), '<p>aa</p>');
});
it('should render mixed lines', function () {
var ops = [new DeltaInsertOp("aa"), new DeltaInsertOp("bb")];
var nlop = new DeltaInsertOp("\n");
var stylednlop = new DeltaInsertOp("\n", { color: '#333', italic: true });
var qdc = new QuillDeltaToHtmlConverter([]);
assert.equal(qdc._renderInlines(ops), '<p>aabb</p>');
var ops0 = [nlop, ops[0], nlop, ops[1]]
assert.equal(qdc._renderInlines(ops0), '<p><br/>aa<br/>bb</p>');
var ops4 = [ops[0], stylednlop, stylednlop, stylednlop, ops[1]]
assert.equal(qdc._renderInlines(ops4),
['<p>aa<br/><br/><br/>bb</p>'].join(''));
});
});
describe('renderBlock()', function () {
var op = new DeltaInsertOp('\n', { header: 3, indent: 2 });
var inlineop = new DeltaInsertOp("hi there");
it('should render container block', function () {
var qdc = new QuillDeltaToHtmlConverter([]);
var blockhtml = qdc._renderBlock(op, [inlineop]);
assert.equal(blockhtml, ['<h3 class="ql-indent-2">',
'hi there</h3>'].join(''));
var qdc = new QuillDeltaToHtmlConverter([]);
var blockhtml = qdc._renderBlock(op, []);
assert.equal(blockhtml, ['<h3 class="ql-indent-2">',
'<br/></h3>'].join(''));
});
it('should correctly render code block', function () {
let ops = [
{
"insert": "line 1"
},
{
"attributes": {
"code-block": true
},
"insert": "\n"
},
{
"insert": "line 2"
},
{
"attributes": {
"code-block": true
},
"insert": "\n"
},
{
"insert": "line 3"
},
{
"attributes": {
"code-block": true
},
"insert": "\n"
},
{
"insert": "<p>line 4</p>"
},
{
"attributes": {
"code-block": true
},
"insert": "\n"
}
]
console.log(encodeHtml("<p>line 4</p>"));
var qdc = new QuillDeltaToHtmlConverter(ops);
let html = qdc.convert();
assert.equal(html, ["<pre>line 1\nline 2\nline 3\n",
encodeHtml("<p>line 4</p>"),
"</pre>"].join("")
);
qdc = new QuillDeltaToHtmlConverter(ops, {
multiLineCodeblock: false
});
html = qdc.convert();
assert.equal(
'<pre>line 1</pre><pre>line 2</pre><pre>line 3</pre>'+
'<pre>'+ encodeHtml("<p>line 4</p>") + '</pre>',
html);
qdc = new QuillDeltaToHtmlConverter([ops[0], ops[1]]);
html = qdc.convert();
assert.equal(html, '<pre>line 1</pre>');
});
});
describe('before n after renders()', function () {
var ops = [
{ insert: 'hello', attributes: { bold: true } },
{ insert: '\n', attributes: { bold: true } },
{ insert: 'how r u?' },
{ insert: 'r u fine' },
{ insert: '\n', attributes: { blockquote: true } },
{ insert: { video: 'http://' } },
{ insert: 'list item 1' },
{ insert: '\n', attributes: { list: 'bullet' } },
{ insert: 'list item 1 indented' },
{ insert: '\n', attributes: { list: 'bullet', indent: 1 } }
]
var qdc = new QuillDeltaToHtmlConverter(ops);
it('should call before/after render callbacks ', function (done) {
let status = {x:0, y:8};
qdc.beforeRender((groupType, data) => {
if (groupType === GroupType.InlineGroup) {
var op = (<any>data).ops[0];
assert.ok(op.attributes.bold);
} else if (groupType === GroupType.Video) {
var op = (<any>data).op;
assert.ok(op.insert.type === 'video');
} else if (groupType === GroupType.Block) {
var d = (<any>data);
assert.ok(d.op.attributes.blockquote && d.ops.length === 2);
} else {
var d = (<any>data);
assert.ok(d.items.length === 1);
}
status.x++;
return '';
});
qdc.afterRender((groupType, html) => {
if (groupType === GroupType.InlineGroup) {
assert.ok(html.indexOf('<strong>hello') > -1);
} else if (groupType === GroupType.Video) {
assert.ok(html.indexOf('<iframe') > -1);
} else if (groupType === GroupType.Block) {
assert.ok(html.indexOf('<blockquote') > -1);
} else {
assert.ok(html.indexOf('list item 1<ul><li') > -1);
}
status.x++;
return html;
});
qdc.convert();
callWhenXEqualY(status, done);
});
it(`should call before render with block grouptype for align
indent and direction`, (done) => {
let ops = [
{insert: 'align'},
{insert: '\n', attributes: {align: 'right'}},
{insert: 'rtl'},
{insert: '\n', attributes: {direction: 'rtl'}},
{insert: 'indent 1'},
{insert: '\n', attributes: {indent: 1}},
];
let status = {x:0, y: 3};
let qdc = new QuillDeltaToHtmlConverter(ops);
qdc.beforeRender((gtype: any) => {
gtype === 'block' && status.x++;
return '';
});
qdc.convert();
callWhenXEqualY(status, done);
});
it('should use my custom html if I return from before call back', function () {
var c = new QuillDeltaToHtmlConverter([
{ insert: { video: "http" } }, { insert: 'aa' }]);
c.beforeRender(() => {
return '<my custom video html>';
});
var v = c.convert();
assert.ok(v.indexOf('<my custom') > - 1);
});
it('should register and use callbacks if they are functions', function () {
var c = new QuillDeltaToHtmlConverter([
{ insert: { video: "http" } }, { insert: 'aa' }]);
var dummy = (): any => '';
c.beforeRender(dummy());
c.afterRender(dummy());
v = c.convert();
assert.ok(v.indexOf('iframe') > -1);
c.beforeRender(dummy);
var v = c.convert();
assert.ok(v.indexOf('<iframe') > - 1 && v.indexOf('aa') > -1);
c.afterRender(dummy);
v = c.convert();
assert.ok(v === '');
});
});
});
});