import { oneLineTrim } from 'common-tags';
import { DOMParser } from 'prosemirror-model';
import WysiwygEditor from '@/wysiwyg/wwEditor';
import EventEmitter from '@/event/eventEmitter';
import CommandManager from '@/commands/commandManager';
import { WwToDOMAdaptor } from '@/wysiwyg/adaptor/wwToDOMAdaptor';
import { cls } from '@/utils/dom';
import type { HTMLConvertorMap } from '@toast-ui/toastmark';
const CODE_BLOCK_CLS = cls('ww-code-block');
describe('wysiwyg commands', () => {
let wwe: WysiwygEditor, em: EventEmitter, cmd: CommandManager;
function setTextToEditor(text: string) {
const { state, dispatch } = wwe.view;
const { tr, doc } = state;
const lines = text.split('\n');
const node = lines.map((lineText) =>
wwe.schema.nodes.paragraph.create(null, wwe.schema.text(lineText))
);
dispatch(tr.replaceWith(0, doc.content.size, node));
}
function setContent(content: string) {
const wrapper = document.createElement('div');
wrapper.innerHTML = content;
const nodes = DOMParser.fromSchema(wwe.schema).parse(wrapper);
wwe.setModel(nodes);
}
beforeEach(() => {
const customHTMLRenderer: HTMLConvertorMap = {
myCustom(node) {
const span = document.createElement('span');
span.innerHTML = node.literal!;
return [
{ type: 'openTag', tagName: 'div', attributes: { 'data-custom': 'myCustom' } },
{ type: 'html', content: span.outerHTML },
{ type: 'closeTag', tagName: 'div' },
];
},
};
const toDOMAdaptor = new WwToDOMAdaptor({}, customHTMLRenderer);
em = new EventEmitter();
wwe = new WysiwygEditor(em, { toDOMAdaptor });
cmd = new CommandManager(em, {}, wwe.commands, () => 'wysiwyg');
});
afterEach(() => {
wwe.destroy();
});
describe('heading command', () => {
it('should add empty heading element', () => {
cmd.exec('heading', { level: 1 });
expect(wwe.getHTML()).toBe('
');
});
it('should add heading element to selection', () => {
setTextToEditor('foo');
cmd.exec('selectAll');
cmd.exec('heading', { level: 2 });
expect(wwe.getHTML()).toBe('foo
');
});
it('should change heading element by level', () => {
setTextToEditor('foo');
cmd.exec('selectAll');
cmd.exec('heading', { level: 3 });
expect(wwe.getHTML()).toBe('foo
');
cmd.exec('selectAll');
cmd.exec('heading', { level: 4 });
expect(wwe.getHTML()).toBe('foo
');
cmd.exec('selectAll');
cmd.exec('heading', { level: 5 });
expect(wwe.getHTML()).toBe('foo
');
cmd.exec('selectAll');
cmd.exec('heading', { level: 6 });
expect(wwe.getHTML()).toBe('foo
');
});
it('should change heading element to paragraph with level 0', () => {
setTextToEditor('foo');
cmd.exec('selectAll');
cmd.exec('heading', { level: 0 });
expect(wwe.getHTML()).toBe('foo
');
});
});
describe('hr command', () => {
it('should add hr element with empty paragraphs in empty document', () => {
cmd.exec('hr');
expect(wwe.getHTML()).toBe(oneLineTrim`
`);
});
it('should add hr element with after empty paragraph', () => {
setTextToEditor('foo');
wwe.setSelection(2, 2);
cmd.exec('hr');
expect(wwe.getHTML()).toBe(oneLineTrim`
foo
`);
});
it('should add only hr element', () => {
setTextToEditor('foo\nbar');
wwe.setSelection(2, 2);
cmd.exec('hr');
expect(wwe.getHTML()).toBe(oneLineTrim`
foo
bar
`);
});
it('should not add hr element when there is selection', () => {
setTextToEditor('foo');
cmd.exec('selectAll');
cmd.exec('hr');
expect(wwe.getHTML()).toBe('foo
');
});
});
describe('blockQuote command', () => {
it('should add blockquote element including empty paragraph', () => {
cmd.exec('blockQuote');
expect(wwe.getHTML()).toBe('
');
});
it('should change blockquote element to selection', () => {
setTextToEditor('foo');
cmd.exec('selectAll');
cmd.exec('blockQuote');
expect(wwe.getHTML()).toBe('foo
');
});
it('should wrap with blockquote element', () => {
setTextToEditor('foo');
cmd.exec('selectAll');
cmd.exec('blockQuote');
cmd.exec('blockQuote');
const expected = oneLineTrim`
foo
`;
expect(wwe.getHTML()).toBe(expected);
});
});
describe('codeBlock command', () => {
it('should add pre element including code element', () => {
cmd.exec('codeBlock');
expect(wwe.getHTML()).toBe(oneLineTrim`
`);
});
it('should change pre element to selection', () => {
setTextToEditor('foo');
cmd.exec('selectAll');
cmd.exec('codeBlock');
expect(wwe.getHTML()).toBe(oneLineTrim`
`);
});
});
describe('bulletList command', () => {
it('should add ul element having empty list item', () => {
cmd.exec('bulletList');
const expected = oneLineTrim`
`;
expect(wwe.getHTML()).toBe(expected);
});
it('should change to bullet list item in selection', () => {
setTextToEditor('foo\nbar\nbaz');
cmd.exec('selectAll');
cmd.exec('bulletList');
const expected = oneLineTrim`
`;
expect(wwe.getHTML()).toBe(expected);
});
});
describe('orderedList command', () => {
it('should add ol element having empty list item', () => {
cmd.exec('orderedList');
const expected = oneLineTrim`
`;
expect(wwe.getHTML()).toBe(expected);
});
it('should change to ordered list item in selection', () => {
setTextToEditor('foo\nbar\nbaz');
cmd.exec('selectAll');
cmd.exec('orderedList');
const expected = oneLineTrim`
foo
bar
baz
`;
expect(wwe.getHTML()).toBe(expected);
});
});
it('bulletList and orderedList command should change parent list to other list when in list item', () => {
setTextToEditor('foo\nbar\nbaz');
cmd.exec('selectAll');
cmd.exec('bulletList');
wwe.setSelection(3, 3); // in 'foo'
cmd.exec('orderedList');
let expected = oneLineTrim`
foo
bar
baz
`;
expect(wwe.getHTML()).toBe(expected);
wwe.setSelection(11, 11); // in 'bar'
cmd.exec('bulletList');
expected = oneLineTrim`
`;
expect(wwe.getHTML()).toBe(expected);
});
describe('taskList command', () => {
it('should add task to ul element ', () => {
cmd.exec('taskList');
const expected = oneLineTrim`
`;
expect(wwe.getHTML()).toBe(expected);
});
it('should change to task item in selection', () => {
setTextToEditor('foo\nbar\nbaz');
cmd.exec('selectAll');
cmd.exec('taskList');
const expected = oneLineTrim`
`;
expect(wwe.getHTML()).toBe(expected);
});
it('should toggle task list item', () => {
setTextToEditor('foo\nbar\nbaz');
cmd.exec('selectAll');
cmd.exec('taskList');
wwe.setSelection(3, 3); // from 'foo'
cmd.exec('bulletList');
let expected = oneLineTrim`
`;
expect(wwe.getHTML()).toBe(expected);
wwe.setSelection(3, 12); // from 'foo' to 'bar'
cmd.exec('taskList');
expected = oneLineTrim`
`;
expect(wwe.getHTML()).toBe(expected);
});
});
describe('bold command', () => {
beforeEach(() => setTextToEditor('foo'));
it('should add strong element to selection', () => {
cmd.exec('selectAll');
cmd.exec('bold');
expect(wwe.getHTML()).toBe('foo
');
});
it('should toggle and remove strong element', () => {
cmd.exec('selectAll');
cmd.exec('bold');
cmd.exec('selectAll');
cmd.exec('bold');
expect(wwe.getHTML()).toBe('foo
');
});
});
describe('italic command', () => {
beforeEach(() => setTextToEditor('foo'));
it('should add emphasis element to selection', () => {
cmd.exec('selectAll');
cmd.exec('italic');
expect(wwe.getHTML()).toBe('foo
');
});
it('should toggle and remove emphasis element', () => {
cmd.exec('selectAll');
cmd.exec('bold');
cmd.exec('selectAll');
cmd.exec('bold');
expect(wwe.getHTML()).toBe('foo
');
});
});
describe('strike command', () => {
beforeEach(() => setTextToEditor('foo'));
it('should add del element to selection', () => {
cmd.exec('selectAll');
cmd.exec('strike');
expect(wwe.getHTML()).toBe('foo
');
});
it('should toggle and remove del element', () => {
cmd.exec('selectAll');
cmd.exec('strike');
cmd.exec('selectAll');
cmd.exec('strike');
expect(wwe.getHTML()).toBe('foo
');
});
});
describe('code command', () => {
beforeEach(() => setTextToEditor('foo'));
it('should add code element to selection', () => {
cmd.exec('selectAll');
cmd.exec('code');
expect(wwe.getHTML()).toBe('foo
');
});
it('should toggle and remove code element', () => {
cmd.exec('selectAll');
cmd.exec('code');
cmd.exec('selectAll');
cmd.exec('code');
expect(wwe.getHTML()).toBe('foo
');
});
});
describe('addImage command', () => {
it('should add image element', () => {
cmd.exec('addImage', {
imageUrl: '#',
});
expect(wwe.getHTML()).toBe('
');
});
it('should add image element with enabled attirbute', () => {
cmd.exec('addImage', {
imageUrl: '#',
altText: 'foo',
foo: 'test',
});
expect(wwe.getHTML()).toBe('
');
});
it('should not add image element when not having imageUrl attribute', () => {
cmd.exec('addImage', {
altText: 'foo',
});
expect(wwe.getHTML()).toBe('
');
});
it('should not decode url which is already encoded', () => {
cmd.exec('addImage', {
imageUrl: 'https://firebasestorage.googleapis.com/images%2Fimage.png?alt=media',
altText: 'foo',
});
expect(wwe.getHTML()).toBe(
'
'
);
});
});
describe('addLink command', () => {
it('should add link element', () => {
cmd.exec('addLink', {
linkUrl: '#',
linkText: 'foo',
});
expect(wwe.getHTML()).toBe('foo
');
});
it('should not add link element when no selection and attributes are missing', () => {
cmd.exec('addLink', {
linkText: 'foo',
});
expect(wwe.getHTML()).toBe('
');
cmd.exec('addLink', {
linkUrl: '#',
});
expect(wwe.getHTML()).toBe('
');
});
it('should change link url in selection', () => {
cmd.exec('addLink', {
linkUrl: '#',
linkText: 'foo bar baz',
});
wwe.setSelection(5, 8);
cmd.exec('addLink', {
linkUrl: 'http://test.com',
linkText: 'bar',
});
const expected = oneLineTrim`
foo
bar
baz
`;
expect(wwe.getHTML()).toBe(expected);
});
it('should not decode url which is already encoded', () => {
cmd.exec('addLink', {
linkUrl: 'https://firebasestorage.googleapis.com/links%2Fimage.png?alt=media',
linkText: 'foo',
});
expect(wwe.getHTML()).toBe(
'foo
'
);
});
});
describe(`addLink command with 'linkAttributes' option`, () => {
beforeEach(() => {
const linkAttributes = {
target: '_blank',
rel: 'noopener noreferrer',
};
const toDOMAdaptor = new WwToDOMAdaptor({}, {});
em = new EventEmitter();
wwe = new WysiwygEditor(em, { toDOMAdaptor, linkAttributes });
cmd = new CommandManager(em, {}, wwe.commands, () => 'wysiwyg');
});
it('should add link element with link attributes', () => {
cmd.exec('addLink', {
linkUrl: '#',
linkText: 'foo',
});
expect(wwe.getHTML()).toBe(
'foo
'
);
});
});
describe('toggleLink command', () => {
beforeEach(() => setTextToEditor('foo'));
it('should add link element to selection', () => {
cmd.exec('selectAll');
cmd.exec('toggleLink', {
linkUrl: 'linkUrl',
});
expect(wwe.getHTML()).toBe('foo
');
});
it('should toggle link element to selection', () => {
cmd.exec('selectAll');
cmd.exec('toggleLink', {
linkUrl: 'linkUrl',
});
cmd.exec('selectAll');
cmd.exec('toggleLink');
expect(wwe.getHTML()).toBe('foo
');
});
});
describe('history command', () => {
beforeEach(() => {
setTextToEditor('foo');
cmd.exec('selectAll');
cmd.exec('bold');
cmd.exec('italic');
});
it('undo go back to before previous action', () => {
cmd.exec('undo');
expect(wwe.getHTML()).toBe('foo
');
cmd.exec('undo');
expect(wwe.getHTML()).toBe('foo
');
});
it('redo cancel undo action', () => {
cmd.exec('undo');
cmd.exec('undo');
cmd.exec('redo');
expect(wwe.getHTML()).toBe('foo
');
});
});
describe('indent command', () => {
let html;
beforeEach(() => {
html = oneLineTrim`
`;
setContent(html);
});
// @TODO move to 'tab' key event test
// it('should add spaces for tab when it is not in list', () => {
// setContent('foo
');
// wwe.setSelection(1, 1);
// cmd.exec( 'indent');
// expect(wwe.getHTML()).toBe(' foo
');
// wwe.setSelection(1, 8);
// cmd.exec( 'indent');
// expect(wwe.getHTML()).toBe('
');
// });
it('should indent to list items at cursor position', () => {
wwe.setSelection(18, 18);
cmd.exec('indent');
const expected = oneLineTrim`
`;
expect(wwe.getHTML()).toBe(expected);
});
it('should indent to list items as selection', () => {
wwe.setSelection(18, 26);
cmd.exec('indent');
const expected = oneLineTrim`
`;
expect(wwe.getHTML()).toBe(expected);
});
});
describe('outdent command', () => {
let html;
beforeEach(() => {
html = oneLineTrim`
`;
setContent(html);
});
// @TODO move to 'shift + tab' key event test
// it('should remove spaces for tab when it is not in list', () => {
// setContent(' foo
');
// wwe.setSelection(4, 4);
// cmd.exec( 'outdent');
// expect(wwe.getHTML()).toBe('foo
');
// setContent('foo bar
');
// wwe.setSelection(6, 6);
// cmd.exec( 'outdent');
// expect(wwe.getHTML()).toBe('foo bar
');
// wwe.setSelection(6, 8);
// cmd.exec( 'outdent');
// expect(wwe.getHTML()).toBe('foobar
');
// });
it('should outdent to list items at cursor position', () => {
wwe.setSelection(19, 19);
cmd.exec('outdent');
const expected = oneLineTrim`
`;
expect(wwe.getHTML()).toBe(expected);
});
it('should outdent to list items as selection', () => {
wwe.setSelection(10, 20);
cmd.exec('outdent');
const expected = oneLineTrim`
`;
expect(wwe.getHTML()).toBe(expected);
});
it('should change list item of 1 depth into paragraph ', () => {
wwe.setSelection(3, 5);
cmd.exec('outdent');
const expected = oneLineTrim`
foo
-
bar
`;
expect(wwe.getHTML()).toBe(expected);
});
});
describe('customBlock command', () => {
it('should add customBlock element', () => {
cmd.exec('customBlock', { info: 'myCustom' });
expect(wwe.getHTML()).toBe(oneLineTrim`
`);
});
it('should change customBlock element to selection', () => {
setTextToEditor('foo');
cmd.exec('selectAll');
cmd.exec('customBlock', { info: 'myCustom' });
expect(wwe.getHTML()).toBe(oneLineTrim`
`);
});
});
});