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`
            foo
          
`); }); }); 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`
  1. foo

  2. bar

  3. 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`
  1. foo

  2. bar

  3. 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('

foo

'); }); 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( '

foo

' ); }); }); 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

  1. bar

`; expect(wwe.getHTML()).toBe(expected); }); }); describe('customBlock command', () => { it('should add customBlock element', () => { cmd.exec('customBlock', { info: 'myCustom' }); expect(wwe.getHTML()).toBe(oneLineTrim`
myCustom
`); }); it('should change customBlock element to selection', () => { setTextToEditor('foo'); cmd.exec('selectAll'); cmd.exec('customBlock', { info: 'myCustom' }); expect(wwe.getHTML()).toBe(oneLineTrim`
foo
myCustom
`); }); }); });