import { Assertions, Keys } from '@ephox/agar'; import { beforeEach, context, describe, it } from '@ephox/bedrock-client'; import { Html, InsertAll, Remove, SugarElement } from '@ephox/sugar'; import { TinyAssertions, TinyContentActions, TinyHooks, TinySelections } from '@ephox/wrap-mcagar'; import Editor from 'tinymce/core/api/Editor'; import * as Utils from '../../module/test/TextPatternsUtils'; describe('browser.tinymce.core.textpatterns.ReplacementTest', () => { const hook = TinyHooks.bddSetupLight({ text_patterns: [ { start: 'brb', replacement: 'be right back' }, { start: 'heading', replacement: '

My Heading

' }, { start: 'complex pattern', replacement: '

Text

More text

' }, { start: '*', end: '*', format: 'italic' }, { start: '#', format: 'h1' }, { start: 'text_pattern', replacement: 'wow' } ], indent: false, base_url: '/project/tinymce/js/tinymce' }, [ ]); beforeEach(() => { const editor = hook.editor(); editor.setContent(''); }); const assertContentAndCursor = (editor: Editor, beforeType: string, afterType?: string) => { const normalize = afterType === undefined; if (normalize) { afterType = beforeType.replace(/<(([^> ]*)[^>]*)>( | )\|<\/\2>/g, '<$1>|').replace(/ /g, ' '); beforeType = beforeType.replace(/\|/g, ''); } TinyAssertions.assertContent(editor, beforeType); editor.insertContent('|'); const content = editor.getContent(); const normalizedContent = normalize ? content.replace(/ /g, ' ') : content; Assertions.assertHtml('Checking cursor', afterType ?? '', normalizedContent); }; it('Apply html replacement pattern on space', () => { const editor = hook.editor(); Utils.setContentAndPressSpace(editor, 'heading'); assertContentAndCursor(editor, '

My Heading

 

', '

My Heading

 |

'); }); it('Apply html replacement pattern on enter', () => { const editor = hook.editor(); Utils.setContentAndPressEnter(editor, 'heading'); assertContentAndCursor(editor, '

My Heading

 |

'); }); it('Apply html replacement pattern on enter in middle of word', () => { const editor = hook.editor(); Utils.setContentAndPressEnter(editor, 'XheadingX', 8); assertContentAndCursor(editor, '

X

My Heading

 

|X

'); }); it('Apply complex html replacement pattern on enter', () => { const editor = hook.editor(); Utils.setContentAndPressEnter(editor, 'complex pattern'); assertContentAndCursor(editor, '

Text

More text

 |

'); }); it('Apply text replacement pattern on space', () => { const editor = hook.editor(); Utils.setContentAndPressSpace(editor, 'brb'); assertContentAndCursor(editor, '

be right back |

'); }); it('Apply text replacement pattern on space with content before', () => { const editor = hook.editor(); Utils.setContentAndPressSpace(editor, 'Xbrb'); assertContentAndCursor(editor, '

Xbe right back |

'); }); it('Apply text replacement pattern on space with content after', () => { const editor = hook.editor(); Utils.setContentAndPressSpace(editor, 'brbX', 3); assertContentAndCursor(editor, '

be right back |X

'); }); it('Do not replace on pattern with content after when cursor is in the wrong position', () => { const editor = hook.editor(); Utils.setContentAndPressSpace(editor, 'brbX'); assertContentAndCursor(editor, '

brbX |

'); }); it('Apply text replacement pattern on enter', () => { const editor = hook.editor(); Utils.setContentAndPressEnter(editor, 'brb'); assertContentAndCursor(editor, '

be right back

 |

'); }); it('Apply text replacement pattern on enter with content before', () => { const editor = hook.editor(); Utils.setContentAndPressEnter(editor, 'Xbrb'); assertContentAndCursor(editor, '

Xbe right back

 |

'); }); it('Apply text replacement pattern on enter with content after', () => { const editor = hook.editor(); Utils.setContentAndPressEnter(editor, 'brbX', 3); assertContentAndCursor(editor, '

be right back

|X

'); }); it('Apply replacement pattern and inline pattern on space', () => { const editor = hook.editor(); Utils.setContentAndPressSpace(editor, '*brb*'); assertContentAndCursor(editor, '

be right back |

'); }); it('Apply replacement pattern and block pattern on enter', () => { const editor = hook.editor(); Utils.setContentAndPressEnter(editor, '#brb'); assertContentAndCursor(editor, '

be right back

 |

'); }); it('Apply replacement pattern italic pattern and block pattern on enter', () => { const editor = hook.editor(); Utils.setContentAndPressEnter(editor, '#*brb*'); assertContentAndCursor(editor, '

be right back

 |

'); }); it('Apply replacement pattern italic pattern and block pattern on enter with fragmented text', () => { const editor = hook.editor(); Utils.setContentAndPressEnter(editor, '#*brb*', 3, [ 0 ]); assertContentAndCursor(editor, '

be right back

 |

'); }); context('Fragmented text nodes in a paragraph', () => { const testFragmentedText = (editor: Editor, pressKey: () => void, getNodes: () => SugarElement[], elementPath: number[], offset: number, expected: string) => { editor.setContent('

'); const paragraph = SugarElement.fromDom(editor.dom.select('p')[0]); Remove.empty(paragraph); InsertAll.append(paragraph, getNodes()); editor.focus(); TinySelections.setCursor(editor, elementPath, offset); pressKey(); TinyAssertions.assertContent(editor, expected); }; const testEnterOnFragmentedText = (editor: Editor, getNodes: () => SugarElement[], elementPath: number[], offset: number, expected: string) => testFragmentedText(editor, () => TinyContentActions.keystroke(editor, Keys.enter()), getNodes, elementPath, offset, expected); const testSpaceOnFragmentedText = (editor: Editor, getNodes: () => SugarElement[], elementPath: number[], offset: number, expected: string) => testFragmentedText(editor, () => { TinyContentActions.keydown(editor, Keys.space()); editor.execCommand('mceInsertContent', false, ' '); TinyContentActions.keyup(editor, Keys.space()); }, getNodes, elementPath, offset, expected); it('TINY-8779: Pattern matches when found in the middle text node', () => { const getNodes = () => [ SugarElement.fromText('text'), SugarElement.fromText('_pattern'), SugarElement.fromText(' for sure') ]; testEnterOnFragmentedText(hook.editor(), getNodes, [ 0, 1 ], 8, '

wow

for sure

'); testSpaceOnFragmentedText(hook.editor(), getNodes, [ 0, 1 ], 8, '

wow  for sure

'); }); it('TINY-8779: Pattern matches when found in the last text node', () => { const getNodes = () => [ SugarElement.fromText('text'), SugarElement.fromText('_pattern') ]; testEnterOnFragmentedText(hook.editor(), getNodes, [ 0, 1 ], 8, '

wow

 

'); testSpaceOnFragmentedText(hook.editor(), getNodes, [ 0, 1 ], 8, '

wow 

'); }); it('TINY-8779: Pattern matches with an inline element amongst text nodes', () => { const getNodes = () => { const strongNode = SugarElement.fromTag('strong'); Html.set(strongNode, 'element'); return [ SugarElement.fromText('first'), SugarElement.fromText(' '), strongNode, SugarElement.fromText(' text_pattern'), SugarElement.fromText(' is big') ]; }; testEnterOnFragmentedText(hook.editor(), getNodes, [ 0, 3 ], 13, '

first element wow

is big

'); testSpaceOnFragmentedText(hook.editor(), getNodes, [ 0, 3 ], 13, '

first element wow  is big

'); }); }); });